2011/12/03

.NET Framework - ListView

สวัสดีท่านผู้อ่านทุกท่านครับ

วันนี้ผมจะมาแนะนำวิธีใช้ ListView ซึ่งเป็น Control ตัวหนึ่งใน WPF ครับ

ก่อนอื่นเลยก็ต้องสร้าง Project ใหม่ เป็น WPF



แล้วก็มาเขียนโค้ด XAML กันเลย~



<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListView x:Name="listView">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="No." Width="Auto" DisplayMemberBinding="{Binding No}" />
                    <GridViewColumn Header="Name" Width="180" DisplayMemberBinding="{Binding Name}"/>
                    <GridViewColumn Header="Uri" Width="250" DisplayMemberBinding="{Binding Uri}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>


ผมได้สร้าง ListView ขึ้นมาหนึ่งตัว แล้วตั้งชื่อว่า listView แล้วกำหนด Column ไว้ 3 ตัว คือ No., Name และ Uri และกำหนดค่าที่อ่านใน Binding



เสร็จแล้วก็มาเขียนโค้ดคำสั่งกันครับ

ก่อนอื่นเลยก็เพิ่ม namespace System.Collections.ObjectModel ด้วยนะครับ


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        ObservableCollection<WebListItem> WebCollection = new ObservableCollection<WebListItem>();
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            listView.ItemsSource = WebCollection;
            WebCollection.Add(new WebListItem { No = 1, Name = "Google", Uri = new Uri("http://www.google.com") } );
            WebCollection.Add(new WebListItem { No = 2, Name = "Google+", Uri = new Uri("http://plus.google.com") });
            WebCollection.Add(new WebListItem { No = 3, Name = "Gmail", Uri = new Uri("http://mail.google.com") });
            WebCollection.Add(new WebListItem { No = 4, Name = "Facebook", Uri = new Uri("http://www.facebook.com") });
            WebCollection.Add(new WebListItem { No = 5, Name = "MSDN", Uri = new Uri("http://msdn.microsoft.com") });
        }
    }

    class WebListItem
    {
        public int No { get; set; }
        public string Name { get; set; }
        public Uri Uri { get; set; }
    }
}


WebListItem เป็น class ที่สร้างขึ้นมาเพื่อเก็บข้อมูล แล้วก็ต้องตั้งชื่อ property ให้ตรงกับที่เขียนใน Binding ด้วยนะครับ
หลังจากนั้นก็สร้าง ObservableCollection<WebListItem> สำหรับเก็บค่าขึ้นมา แล้วกำหนดให้ listView ของเราดึงข้อมูลจาก collection ที่เราสร้าง

เป็นยังไงบ้างครับ ใช้ไม่ยากเลยใช่ไหมหล่ะครับ




2011/12/02

.NET Framework - Memory Leak เวลาใช้ System.Windows.Controls.Image

สวัสดีครับ เมื่อไม่กี่วันมานี้ผมเจอปัญหา Memory Leak ตอนใช้ System.Windows.Controls.Image ใน WPF มาครับ มาดูกันว่าผมทำยังไงมันถึงเกิดปัญหาขึ้น

ปัญหามีอยู่ว่า ผมโหลดภาพด้วย BitmapImage จำนวน 10 ภาพแล้วแสดงผลด้วย Image และผมต้องการที่จะลบภาพออก ปรากฏว่าเกิด Memory Leak ขึ้น โปรแกรมผมกิน Ram ไป เท่ากับตอนที่โหลดภาพ 10 ภาพ

เพื่อความสะดวกผมจะขอโหลดภาพขึ้นมาแค่ภาพเดียว เป็นไฟล์ png ขนาด 5.27MB ความละเอียด 1810x4034


public partial class MainWindow : Window
{
    Image img;

    public MainWindow()
    {
        InitializeComponent();
        img = new Image();
        stackPanel1.Children.Add(img);
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var bi = new BitmapImage();
        bi.BeginInit();
        bi.UriSource = new Uri(@"H:\img.png");
        bi.EndInit();
        if (bi.CanFreeze)
            bi.Freeze();
        img.Width = bi.PixelWidth;
        img.Height = bi.PixelHeight;
        img.Source = bi;
        bi = null;
        img.Visibility = System.Windows.Visibility.Visible;
    }

    private void button2_Click(object sender, RoutedEventArgs e)
    {
        img.Source = null;
        img.Visibility = System.Windows.Visibility.Collapsed;
    }
}

Code ข้างบนนี้เกิด Memory Leak


ผมลองเอา

img.Visibility = System.Windows.Visibility.Collapsed;

ออกปรากฏว่าปัญหา Memory Leak หายไป แต่ตัว Image ยังจองพื้นที่ใน Window อยู่จึงต้องกำหนดความกว้างกับความสูงเป็น 0 แทน

private void button2_Click(object sender, RoutedEventArgs e)
{
    img.Source = null;
    img.Width = img.Height = 0.0;
}

เท่านี้ก็ช่วยลด Memory ที่ใช้แล้ว ถึงจะลดได้ไม่หมด แม้ว่าจะลบ Image ออก แล้วใช้ GC.Collect() หน่วยความจำก็ไม่กลับคืนมาทั้งหมด
จะเห็นว่าตอนเปิดโปรแกรมใหม่ ๆ ใช้ Ram 14,596K
พอโหลดภาพมาแสดงใช้ Ram 72,984K
พอลบภาพออกจะใช้ Ram ลดลงเหลือ 45,032K

ตอนนี้ผมก็ยังไม่รู้จะแก้ได้ยังไง

2011/11/16

.NET Framework - #2 Windows Application


สวัสดีครับ เป็นยังไงกันบ้างครับ
วันนี้เราจะมาสร้าง Form ใน Windows กันครับ

เหมือนเดิมนะครับ ให้สร้าง Project เปล่า ๆ ขึ้นมา แล้วก็เพิ่ม References 3 ตัวคือ System, System.Drawing และ System.Windows.Forms

สำหรับ C++/CLI ให้เพิ่มตรง Project Properties นะครับ


F#, C#, VB.NET ก็เพิ่มตรง References ได้เลยครับ



แล้วก็ไปตั้งค่าอีกนิดนึงครับ
C#, VB.NET, F# ให้เข้าไปที่ Project Properties แล้วตั้ง Output type เป็น Windows Application



ส่วน C++/CLI ให้เข้าไปที่ Project Properties, ในส่วนของ Linker ตรง System เปลี่ยน SubSystem เป็น Windows และตรง Advanced เปลี่ยน Entry Point เป็น main




ตั้งค่าเสร็จแล้ว ก็มาเขียนโค้ดกันเลยครับ~~~~!!!


C++/CLI

using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;

[STAThread]
int main()
{
    Form ^f = gcnew Form();
    f->Text = "C++/CLI Form";
    Application::Run(f);
    return 0;
}


C#

using System;
using System.Drawing;
using System.Windows.Forms;

class Program
{
    [STAThread]
    static void Main()
    {
        Form f = new Form();
        f.Text = "C# Form";
        Application.Run(f);
    }
}


VB.NET

Option Strict On
Option Explicit On

Imports System
Imports System.Drawing
Imports System.Windows.Forms

Module Program
    <STAThread>
    Sub Main()
        Dim f As New Form()
        f.Text = "VB.NET Form"
        Application.Run(f)
    End Sub
End Module


F#

open System
open System.Drawing
open System.Windows.Forms

[<STAThread>]
let f = new Form(Text = "F# Form")
Application.Run(f)



เป็นยังไงกันบ้างครับ เห็นไหมครับว่าภาษา .NET เหมือนกันโค้ดแทบไม่ต่างกันเลย ต่างกันแค่ไวยากรณ์ของภาษานั้น ๆ

วันนี้ก็เอาไว้เท่านี้นะครับ ขอให้สนุกกับการเขียนโปรแกรมครับผม ^w^

(โปรแกรมที่ผมใช้วันนี้คือ Visual Studio 11 Developer Preview นะครับ)

2011/11/11

F# - #3

สวัสดีครับ วันนี้เราก็มาฝึกเขียนภาษา F# กันเหมือนเดิมนะครับ
ก่อนอื่นผมจะขอพูดเกี่ยวกับฟังก์ชันอีกหน่อยนะครับ
คราวนี้ให้เปิดตัว F# Interactive (Console) นะครับ



แล้วลองพิมพ์ลงไปว่า

let f a b = a + b;;
let g(a, b) = a + b;;

จะเห็นว่า f กับ g จะไม่เหมือนกัน
ลองมาดูที่ฟังก์ชัน f กันก่อน

val f : int -> int -> int

หมายถึง f จะรับค่า int 2 ตัว แล้วส่งค่า int กลับมา
ส่วน g

val g : int * int -> int

หมายถึง g จะรับค่า int * int ที่เป็น tuple แล้วจะส่งค่า int กลับออกมา



คราวนี้เราลองมากำหนดชนิดของค่าที่รับเข้าไปกัน

let f (a : float) (b : float) = a + b;;

val f : float -> float -> float

จะเห็นว่าผลลัพท์ (ค่าที่ฟังก์ชันส่งกลับมา) จะเปลี่ยนด้วย

คราวนี้เรากลับมาที่ Visual Studio กันนะครับ
วันนี้เราจะมาดูวิธีเขียนเงื่อนไขกัน

let a = 10
if a < 20 then
    printfn "a < 20"
else
    printfn "a >= 20"

วิธีเขียนจะคล้าย ๆ Object Pascal กับ VB.NET

ส่วนเครื่องหมายจะมี
= เท่ากับ
< น้อยกว่า
> มากกว่า
<> ไม่เท่ากับ

แต่ถ้ามีหลายเงื่อนไข จะใช้ && (and) กับ || (or) เป็นตัวเชื่อม และ elif จะหมายถึง else if

let a = 10
if a < 20 && a >= 0 then
    printfn "0 <= a < 20"
elif a >= 20 then
    printfn "a >= 20"
else
    printfn "a < 0"

คราวนี้มาดูวิธีตรวจสอบค่ากันบ้าง ที่ในภาษา C/C++, C# เป็น switch case แต่ใน F# จะใช้ match with

let a = 4
match a with
| 0 -> printfn "Zero"
| 1 -> printfn "One"
| 2 | 3 -> printfn "2 or 3"
| x when a > 3 -> printfn "%i" x
| _ when a < -4 -> printfn "< -4"
| _ -> printfn "Other"

เครื่องหมาย | (pipe) จะเหมือนกับ case ในภาษา C#
| x when a > 3 หมายถึง ให้ค่าเป็น x ถ้า a > 3 หรือจะเขียนเป็น | x when x > 3 ก็ได้
| _ when a < -4 ไม่สามารถเขียนเป็น _ when _ < -4 ได้
นอกจากนี้ยังเขียนเป็น | x when x > 3 && x < 6 ได้อีกด้วย

ลองเขียนเคสอื่น ๆ ดูนะครับ

คราวนี้ลองมาดู resursive function (ฟังก์ชันเรียกตัวเอง) กันบ้าง

let rec fac n =
    match n with
    | _ when n < 0 -> failwith "n must >= 0"
    | 0 | 1 -> 1
    | _ -> n * fac(n - 1)

เราจะใช้ rec ต่อหลัง let เพื่อบอกว่า ฟังก์ชันที่จะสร้างเป็นฟังก์ชันแบบ recursive ครับ
ส่วน failwith หมายถึง ให้แสดง error ครับ ลองใส่ค่าติดลบดูก็ได้ครับ
ส่วนที่ต้องใส่วงเล็บ ตรง fac(n - 1) ก็เพราะว่า ถ้าเราเขียนเป็น n * fac n - 1 มันจะกลายเป็น n * fac(n) - 1

คราวนี้เราลองมารับค่ากันบ้างครับ
ก่อนอื่นเราต้องเรียก namespace System ขึ้นมาก่อน ด้วยคำสั่ง open เหมือนกับ using ใน C# และ Import ใน VB.NET

open System

printf "Enter a number: "
let a = int(Console.ReadLine())
printfn "%i" a

คราวนี้ถ้าจะรับข้อความเข้ามาทั้งบรรทัด

open System

printf "Enter a string: "
let a = Console.ReadLine()
printfn "%s" a

และเราจะแยกข้อความออกจากกันถ้าเจอเว้นวรรค

open System

printf "Enter a string: "
let a = Console.ReadLine().Split()
printfn "%A" a

ตอนนี้เราจะได้ a เป็น array นะครับ จึงไม่สามารถใช้ %s กับ a ได้
จากตรงนี้ลองพิมพ์ข้อความลงไปว่า Hello World
เราจะได้ [|"Hello"; "World"|]
ซึ่งเราจะเขียนแบบนี้ตอนสร้าง array ครับ

let a = [|1; 2; 3; 4|]
printfn "%A" a

สมาชิกแต่ละตัวจะใช้ ; (semi-colon) เป็นคั่น และใช้ [|   |] ครอบ

แล้วถ้าต้องการรับค่าเข้ามาแล้วแปลงเป็น int หล่ะจะทำยังไง
เช่น ใส่ค่าเข้ามาว่า 1 6 12 จะให้รับเข้ามาเป็น array [|1; 6; 12|]

open System

printf "Enter a string: "
let a = Array.map(fun x -> int x) (Console.ReadLine().Split())
printfn "%A" a

เราจะใช้คำสั่ง Array.map เข้ามาช่วย แล้วเขียนฟังก์ชันข้างใน
หรือจะเขียนว่า

let a = Console.ReadLine().Split() |> Array.map(fun x -> int x)

ก็ได้

แล้วเจ้า |> (pipe + >) มันคืออะไร ?

เครื่องหมาย |> นิยามโดย

let (|>) x f = f x

ลองดูกับฟังก์ชันง่าย ๆ

open System
let a = Math.Cos 5.0
printfn "%f" a

เราจะเขียนเป็น

let a = 5.0 |> Math.Cos

ก็ได้ มีความหมายเหมือนกัน

เริ่มงงกันแล้วใช่ไหมครับ งั้นวันนี้พอแค่นี้ก่อนละกันครับ เดี๋ยวผมจะมาพูดเรื่อง Array อีกทีนะครับ

2011/11/10

F# - #2

สวัสดีครับ วันนี้เราจะมาฝึกเขียนภาษา F# กันอีกเช่นเคย แต่จะเป็นเรื่องอะไร ลองทายดูสิครับ
...
วันนี้เราจะมาเขียนฟังก์ชันกันครับ !!!!
อ่านถูกแล้วหล่ะครับ มันคือฟังก์ชัน นั้นแหละครับ
บางคนอาจจะงง ยังไม่ทันไรก็ขึ้นฟังก์ชันแล้ว ?
จริง ๆ แล้วฟังก์ชันในภาษา F# นั้นสร้างไม่ยากเลยครับ เพราะว่าใช้คำสั่ง let เหมือนกัน

let myFun() = 5
let a = myFun()
printfn "%A" a

เหมือนกันเลยครับ แต่ต้องใส่ () ต่อท้ายเท่านั้นเอง เราก็จะได้ myFun เป็นฟังก์ชันแล้ว... เย้

ต่อไปมาลองสร้างฟังก์ชันที่มีการรับค่าเข้าไปดูครับ

let add a b = a + b
let r = add 6 4
printfn "%A" r

เป็นยังไงบ้างครับ ง่ายใช่ไหมหล่ะครับ

แล้วถ้าฟังก์ชันมีหลายบรรทัดหล่ะ จะเขียนยังไงดี ?

ก่อนอืนเลยต้องบอกว่า scope ใน F# จะใช้ย่อหน้าครับ เช่นในภาษา C/C++, C# จะเป็น { } ใน Object Pascal เป็น begin end; เป็นต้น

let f x =
    let r1 = x * x
    let r2 = r1 + 20
    r2
printfn "%A" (f 6)

จะเห็นว่าคำสั่งที่อยู่ในย่อหน้า ตั้งแต่ let r1 จนถึง r2 จะเป็นคำสั่งในฟังก์ชัน f ครับ

ลองมาดูกันทีละบรรทัดนะครับ
let f x =

หมายถึง สร้างฟังก์ชัน f ที่รับค่า x เข้าไป
    let r1 = x * x
    let r2 = r1 + 20

คือการสร้างค่าคงที่ ที่อยู่ในฟังก์ชันครับ เพราะฉะนั้นเราจึงใช้ r1 กับ r2 ได้แค่ในฟังก์ชัน f เท่านั้น

    r2

คือการส่งค่ากลับออกมาให้ฟังก์ชันครับ คล้าย ๆ กับ return r2; ในภาษา C/C++, C# ฯลฯ

นอกจากนี้เรายังสามารถประกาศค่าคงที่ ที่ชื่อซ้ำกันในฟังก์ชันได้อีกด้วย

let f x =
    let r = x * x
    let r = r + 20
    r

คำสั่งนี้จะเหมือนกับคำสั่งก่อนหน้านี้เลยครับ
จริง ๆ แล้วเราจะใส่วงเล็บตอนประกาศฟังก์ชันหรือตอนเรียใช้ก็ได้นะครับ

let f(x) =
    let r = x * x
    let r = r + 20
    r
printfn "%A" (f(6))

นอกจากนี้ยังสามารถใช้ชื่อซ้ำกันใน scope ที่ซ้อนกันได้

let f x =
    let r =
        let r = x * x
        r + 1
    r * 2
printfn "%A" (f 3)

จะเห็นว่าตำสั่ง r + 1 จะหมายถึง r ที่เป็น x * x และ r * 2 จะหมายถึง r ตัวแรกที่อยู่หลัง let f x


Tuple คืออะไร ? http://en.wikipedia.org/wiki/Tuple

ลองดูตัวอย่างกันดีกว่าครับ
let (a, b) = (1, 2) // หรือ let a, b = 1, 2
printfn "a = %A" a
printfn "b = %A" b

เราจะได้ a = 1 และ b = 2 ครับ คิดว่าดูจากตัวอย่างแล้วน่าจะเข้าใจนะครับ

คราวนี้
let a = (1, 2)
printfn "%A" a

ลองรันดูนะครับ แล้วดูว่า a คืออะไร ?

ถ้ารันดูแล้วจะรู้ว่า a จะเท่ากับ (1, 2)

แล้วจะเอาค่าออกมายังไงหล่ะ ?
let a = (1, 2)
let x = fst a
let y = snd a
printfn "x = %A\ny = %A" x y

เราจะใช้ฟังก์ชัน fst เพื่อเอาค่าของตัวแรกออกมา และใช้ snd เพื่อเอาค่าตัวหลังออกมา ซึ่งฟังก์ชันทั้ง 2 ตัวนี้มีอยู่แล้วครับ แต่ถ้าใครจะเขียนเองก็ไม่ยากครับ
let fst(a, b) = a
let snd(a, b) = b

แต่ต้องใส่วงเล็บด้วยนะครับ จะเขียนเป็น fst a b ไม่ได้ เพราะ (a, b) มันคือ tuple ไงครับ

ลองดูตัวอย่างกันอีกหน่อย
let f a b = a + b
let x = f(4, 5)
printfn "%A" x

เราไม่สามารถเขียนแบบนี้ได้นะครับ เพราะ f จะต้องรับค่าเข้าไป 2 ค่า แต่ (4, 5) มันคือ ค่าเดียวครับ
จึงต้องเขียนเป็นแบบนี้
let f(a, b) = a + b
let x = f(4, 5)
printfn "%A" x

และแน่นอนครับว่าแบบข้างล่างนี้ก็ใช้ไม่ได้
let f(a, b) = a + b
let x = f 4 5
printfn "%A" x

เพราะว่า f รับค่าเข้าไปแค่ค่าเดียว คือข้อมูลแบบ tuple (a, b) แต่ f 4 5 หมายถึง ส่ง 4 กับ 5 เข้าไปให้ f


ต่อไปมาดูการละค่ากันครับ
let a = (1, 2)
let x, _ = a
printfn "%A" x

จะได้ x = 1 ครับ เครื่องหมาย _ (underscore) หมายถึงละค่าครับ เพราะฉะนั้นเลข 2 จังไม่ถูกเก็บเข้ามา

และ _ ไม่สามารถตั้งเป็นชื่อได้ เราจึงไม่สามารถ printfn "%A" _ ได้ครับ

2011/11/09

F# - #1

สวัสดีครับ วันนี้เราจะมาหัดเขียนโปรแกรมในภาษา F# กันครับ
ภาษา F# คืออะไร ? ลองดูที่ wikipedia นะครับ http://en.wikipedia.org/wiki/F_Sharp_(programming_language)
ก่อนอื่นเลยเราต้องมาลงเจ้า Compiler กับ IDE กันก่อนนะครับ
ตัว Compiler โหลดได้จาก http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/ หรือถ้าหาไม่เจอก็เข้า http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=18706 ก็ได้ครับ
ส่วน IDE ก็ใช้ Visual Studio 2010 Shell ครับ http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=115
แล้วก็อย่าลืมลง .NET Framework ด้วยนะครับ http://www.microsoft.com/download/en/details.aspx?id=17851
เอาหล่ะ เราลองมาเขียนโปรแกรมแรกกันเลยดีกว่า ลองทายสิว่าโปรแกรมอะไร...

ใช่แล้ว มันก็คือ Hello World นั้นเอง
ก่อนอื่นเลยก็เปิดโปรแกรม Visual Studio 2010 Shell สร้าง Project ใหม่นะครับ เลือกที่ F# Application แล้วกด OK โล้ดดด




แล้วเราจะได้ source code เปล่า ๆ ที่ comment 1 บรรทัด เขียนไว้ว่า
// Learn more about F# at http://fsharp.net

จะลบทิ้งก็ได้นะครับ

สำหรับคนที่เคยเขียนภาษาอื่นมาก่อนเช่น C# หรือ VB.NET อาจจะตกใจเพราะว่าไม่เจอ code อะไรเลย ปกติใน C# หรือ VB.NET เมื่อสร้าง project ใหม่แล้วจะมี template มาให้

เอาละครับ เรามาเขียน code กันเลย

ง่าย ๆ สั้น ๆ ครับ ไม่ต้องสร้าง class ไม่ต้องสร้าง function อะไรเลย แค่พิมพ์ลงไปว่า
printfn "Hello World"

ไม่ต้องตกใจครับ แค่บรรทัดเดียวครับ "code มีแค่บรรทัดเดียว"

เสร็จลองรันดูนะครับ ถ้ารันแบบ Debug (F5) จะมีหน้าต่างดำ ๆ โผล่ออกมาแล้วหายไป ให้รันแบบไม่ใช้ Debug (Ctrl + F5) แทนนะครับ



เป็นยังไงบ้างครับ สั้นดีใช่ไหมหล่ะครับ



เอาหล่ะครับ เรามาสร้างค่าคงที่กันดีกว่าครับ...

แน่นอนครับ ว่ามันคือ "ค่าคงที่"

เราก็เจอกันไปแล้วในโปรแกรมที่เขียนกันเมื่อกี้ ก็คือ "Hello World" นี่แหละ มันคือค่าคงที่ชนิด String นี่แหละครับ



และ!! เราจะใช้คำสั่ง let (ให้) ในการตั้งชื่อให้กับค่าคงที่ครับ
let s = "Hello World"

เราจะได้ s เป็นค่าคงที่ ที่เก็บ "Hello World" เอาไว้

ซึ่งแน่นอนว่า s เป็นค่าคงที่ !! เราจึงไม่สามารถเปลี่ยนแปลงค่า s ได้

นี่คือแนวคิดแบบ functional programming ครับ หรือก็คือ ทุก ๆ อย่างนั้นเป็นค่าคงที่ รวมถึง function ด้วย

เราคงเคยเจอกันมาเยอะแล้ว..

เคยเจอมาเยอะแล้วจริง ๆ ครับ ในวิชาคณิตศาสตร์ยังไงหล่ะครับ เช่น ถ้า a = 10 แล้วเราก็ไม่สามารถเปลี่ยนค่า a ได้ถูกไหมครับ



แต่ F# ไม่ได้เป็น functional language ซะดีเดียว เราจึงสามารถเปลี่ยนแปลงค่าได้

เราจะใช้คำสั่ง mutable (ไม่แน่นอน) ต่อท้าย let ครับ เพื่อให้เราสามารถเปลี่ยนแปลงค่าได้
let mutable a = 6

เราจะได้ a ที่แปลงร่างกลายเป็นตัวแปรแล้ว !

แต่ระวังอย่างหนึ่งนะครับ เครื่องหมาย = ไม่ใช่หมายถึง ให้ a = 6 ซะทีเดียว

เครื่องหมาย = คือเครื่องหมายกำหนดค่าให้ตั้งชื่อค่าคงที่ (ในคำสั่ง let) และเป็นเครื่องหมายทางตรรกศาสตร์

เช่นถ้าเราเจอ a = 4 โดยที่ไม่มี let แสดงว่า เครื่องหมาย = กำลังทำหน้าที่ตรวจสอบค่าว่า a เท่ากับ 4 รึเปล่า

เพราะฉะนั้นเราจึงไม่สมารถใช้ = เปลี่ยนค่าของ mutable ได้ แต่จะใช้เครื่องหมาย <- (น้อยกว่า + ขีด) แทน
let mutable a = 6
printfn "%A" a
a <- 10
printfn "%A" a

และแน่นอนว่า ตอนนี้ a มีชนิดข้อมูลเป็น int (จำนวนเต็ม) เราไม่สามารถเปลี่ยนค่าให้กลายเป็นอย่างอื่นได้ เช่น
a <- "Text"
a <- 3.2

ทำไม่ได้แน่นอนครับ



แล้ว... %A คืออะไร ?

คนที่เคยเขียนภาษา C/C++ มาก่อนจะรู้ได้ทันทีเลยว่าคืออะไร (รึเปล่า)

มันก็คล้าย ๆ กับ %d %f ในภาษา C/C++ นั้นแหละ

แต่ %A จะดีกว่าหน่อยนึง ก็คือเราจะใส่ค่าอะไรลงไปก็ได้ เดี๋ยวมันจะเรียกฟังก์ชัน ToString() ของค่านั้น ๆ ออกมาให้

2011/11/06

.NET Framework - #1 Hello World.

สวัสดีครับ หายหน้าหายตาไปนาน ลืมผมหรือยังครับเนี่ย ฮ่า ๆ ๆ

วันนี้เราจะมาเขียนโปรแกรมบน .NET Framework กันครับ จะใช้ภาษาอะไรเลือกกันตามสบายเลยครับ

.NET Framework คืออะไร ?
    .NET Framework ก็คือ platform ที่พัฒนาโดย Microsoft เพื่อที่จะให้การพัฒนาโปรแกรมทำได้ง่ายขึ้น โดยที่เวลา compile ตัว compiler จะแปล code ในภาษาที่เราเขียนเป็นภาษากลาง เรียกว่า MSIL เวลาเรียกใช้โปรแกรมตัว CLR ใน .NET Framework จะแปล MSIL ให้เป็นภาษาเครื่องอีกที

ก่อนอื่นเลยต้องลงโปรแกรม Visual Studio 2010 ก่อนครับ จะใช้เวอร์ชัน Express หรือจะเป็นเวอร์ชันทดลองใช้ก็ได้ครับ โหลดได้ที่ http://www.microsoft.com/visualstudio/en-us

ผมขอใช้ Visual Studio 2010 Pro Trial นะครับ




ลงเสร็จแล้วเราลองมาเขียนโปรแกรมง่าย ๆ กันครับ
เป็นโปรแกรมแสดงข้อความคำว่า Hello World. ผ่าน Console

C++/CLI
สำหรับภาษา C++/CLI นะครับ ให้สร้าง Project เป็นแบบ CLR Empty Project นะครับ แล้วสร้างไฟล์ .cpp ขึ้นมาหนึ่งไฟล์ อาจจะตั้งชื่อว่า main.cpp ก็ได้

using namespace System;

int main()
{
    Console::WriteLine("Hello World.");
    Console::ReadKey();
    return 0;
}


C#
ให้สร้าง Empty Project แล้วสร้าง Code ไฟล์ขึ้นมาใหม่นะครับ

using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("Hello World.");
        Console.ReadKey();
    }
}

VB
ให้สร้าง Empty Project แล้วสร้าง Code ขึ้นมาเหมือนกันครับ แล้วก็อย่าลืมไปตั้งใน Project Properties เปลี่ยนตรง Application type เป็น Console.Application ด้วยนะครับ

Option Strict On
Option Explicit On

Imports System

Module Program
    Sub Main()
        Console.WriteLine("Hello World.")
        Console.ReadKey()
    End Sub
End Module

F#
สร้าง Project เป็นแบบ F# Application นะครับ
ในภาษา F# ผมขอใช้ printfn แทน Console.WriteLine นะครับ

open System

printfn "Hello World."
Console.ReadKey() |> ignore

2011/10/04

C++ - String

สวัสดีผู้อ่านทุกท่านครับ วันนี้ผมจะมาแนะนำ class ตัวหนึ่ง ที่คิดว่าจะทำให้การเขียนโปรแกรมง่ายขึ้น
และเพื่อให้เข้าใจเรื่อง class มากขึ้น

class ที่ผมจะมาแนะนำให้รู้จักคือ string

class string ใน C++ ไม่เหมือนกับ string ใน ภาษา C
ในภาษา C ตัวแปร string คือแถวลำดับของ char
แต่ string ใน C++ จะจัดการกับหน่วยความจำให้กับเราเอง เราไม่ต้องไปยุ่งกับมัน

ก่อนอื่นเลย เราจะต้อง include ไฟล์ที่จำเป็นเข้ามาก่อน คือ

#include <string>

string อยู่ใน namespace std เวลาจะประกาศ object เราจะต้องข้ึนต้นด้วย std

std::string s;

หรือจะประกาศ using namespace std; ไว้เลยก็ได้

using namespace std;


string s;

ลองมาดูฟังก์ชันที่ใช้บ่อยใน string กัน

string::size()
string::length()

ทั้งสองฟังก์ชันนี้จะ return ค่าความยาวของข้อความออกมา
เช่น

s = "Test String";
cout << s.length() << endl;

จะได้ผลลัพท์คือ 11

string::max_size()

จะ return ค่าความยาวของข้อความทั้งหมดที่ string จะสามารถเก็บได้

string::resize(size_t n)
string::resize(size_t n, char c)

จะเป็นการเปลี่ยนขนาดของ string และถ้าเป็นการเพิ่มขนาด จะเอา c ไปต่อท้าย
แต่ถ้าไม่ได้ใส่ค่า c ค่า default คือ '\0'

s = "Test String";
s.resize(7);      // s = "Test St"
s.resize(9, '_'); // s = "Test St__"
s.resize(12);     // s = "Test St__"


string::clear()


เป็นการลบค่าใน string ออก


string::empty()


เป็นการตรวจสอบว่า string มีข้อมูลหรือไม่


string::at(size_t pos)
string::operator[]


คือ char ตัวที่ pos ของ string เช่น s[2]


string::operator+=


เป็นการต่อ string เช่น


s = "String1";
s += " String2";
// s = "String1 String2";


มีความหมายเหมือนกับ


s = s + " String2";


เป็นยังไงกันบ้างครับ string ใช้ง่ายใช่ไหมหล่ะ


เรามาต่อเรื่อง class กันดีกว่า


คราวนี้ผมจะพูดเรื่องการนำฟังก์ชันใน class ออกมาเขียนข้างนอก
จำได้ไหมครับ ว่า class เขียนยังไง ?
คราวนี้ผมจะเขียนแต่ prototype ของฟังก์ชัน


class triangle {
    private:
        float b, h;
    public:
        void setBase(float);
        void setHight(float);
        float getArea();
};


ผมจะนิยามฟังก์ชันข้างนอก ต้องใช้ เครื่องหมาย scope (::) ขั่นระหว่างชื่อ class กับชื่อฟังก์ชัน



void triangle::setBase(float base)
{
    b = base;
}

void triangle::setHight(float hight)
{
    h = hight;
}

float triangle::getArea()
{
    return 0.5 * b * h;
}

ทำไมถึงต้องทำแบบนี้ ?
เพราะว่าถ้าในฟังก์ชันมีคำสั่งเยอะมาก จะทำให้เรามองภาพรวมของ class ยาก
ถ้าใน class มีแต่ prototype และตัวแปร เราจะมองเห็นภาพรวมง่ายขึ้น



หวังว่าบทความนี้จะมีประโยชน์สำหรับใครหลาย ๆ คน นะครับ

2011/10/03

C++ - Classes

จาก Data Structure ที่ผมได้กล่าวไปเมื่อคราวที่แล้ว ยังจำกันได้ไหมเอ่ย

Data Structure คือ กลุ่มของข้อมูลหลาย ๆ ชนิดมารวมอยู่ด้วยกัน
และ Class ก็เหมือน Structure คือกลุ่มของข้อมูลหลาย ๆ ชนิด แต่ยังมี Method อีกด้วย

สรุปคือ
    Data Structure = Data
    Class = Data + Methods

เพื่อความเข้าใจ ผมจะขอยกตัวอย่าง

ยังจำกันได้ไหมว่า structure ประกาศยังไง ?

struct human {
    char name[255];
    int age;
};

ถ้าเราต้องการที่จะประกาศ class เราก็แค่เปลี่ยนคำว่า struct เป็น class

class human {
    char name[255];
    int age;
};

เหมือนกันเลย แค่เปลี่ยนตัวหน้านิดเดียว

แต่ !! เมื่อเราสร้าง object (ตัวแปรของ class เราจะไม่เรียกว่าตัวแปร แต่จะเรียกว่า object แทน) ของ class human เราไม่สามารถที่จะเรียกสมาชิกในนั้นได้...

อย่างเช่น

human a;

เราจะไม่สามารถเรียก

a.name = "Name";

ได้ ทำไมถึงเป็นแบบนี้ ?

คำตอบคือ เพราะว่า class มีการปกป้องข้อมูลของตนเอง เพื่อจำกัดสิทธิ์ในการเข้าถึง

ในภาษา C++ รูปแบบสิทธิ์ของการเข้าถึงข้อมูลจะมี 3 แบบ (ใน Object Pascal มี 4 แบบ)
ได้แก่
    - private
    - public
    - protected

เมื่อเราสร้างตัวแปรใน class แล้วไม่ได้กำหนดขอบเขตการเข้าถึง มันจะถูกกำหนดให้เป็น private โดยอัตโนมัติ

เมื่อขอบเขตถูกกำหนดเป็น private ตัวแปรนั้น ๆ จะไม่สามารถถูกเข้าถึงจากภายนอกได้ เราจึงไม่สามารถเรียก a.name ได้

ถ้าเราต้องการให้ตัวแปรนั้น ๆ สามารถถูกเรียกจากภายนอกได้ เราต้องกำหนดสิทธิ์เป็น public
เราจะได้หน้าตาของ class human แบบใหม่

class human {
    public:
        char name[255];
        int age;
};

คราวนี้เราก็สามารถเรียก a.name ได้แล้ว

ถ้าเราต้องการกำหนดขอบเขตเป็น private ก็แค่ใส่ private: เข้าไป

ส่วน protected ผมขอยังไม่พูดถึงตอนนี้


แล้ว Method คืออะไร ?

ถ้าจะให้พูดง่าย ๆ เลย มันก็คือฟังก์ชันนั่นแหละ

ของดูตัวอย่าง class นี้

class triangle {
    private:
        float b, h;
    public:
        void setBase(float base)
        {
            b = base;
        }
        void setHight(float hight)
        {
            h = hight;
        }
        float getArea()
        {
            return 0.5 * b * h;
        }
};

จะเห็นว่า เราไม่สามารถจะเรียกใช้ตัวแปร b กับ h ใน class นี้ได้
แต่เราสามารถเข้าถึงผ่านทาง function ที่อยู่ใน class แทน

ทำไมถึงต้องทำแบบนี้ ?
ก็เพราะ เพื่อไม่ให้ข้อมูลถูกแก้ไขตามใจชอบ สามารถป้องกันการเปลี่ยนข้อมูลแล้วเกิดข้อผิดพลาดได้อีกด้วย

เมื่อเราจะใช้เราก็เรียกผ่านฟังก์ชันพวกนี้ เช่น

triangle s;
s.setBase(4.2);
s.setHight(6.0);
cout << s.getArea() << endl;

class จะช่วยให้การเขียนโปรแกรมมีความยืดหยุดมากขึ้น
ยังเหลืออีกหลาย ๆ เรื่องเกี่ยวกับ class ที่ผมไม่ได้ยกมาพูดตอนนี้
แต่ผมจะมาพูดให้ฟังในคราวหน้า หวังว่าทุกท่านจะสนุกกับการสร้าง class นะครับ ^_^

C++ - Data Structures

เรารู้จักกลุ่มของข้อมูลชนิดเดียวกันมาแล้ว เรียกว่า array
คราวนี้ผมจะแนะนำให้รู้จักกลุ่มของข้อมูลต่างชนิดกัน ที่เรียกว่า Data Structure

Data Structure คือ กลุ่มของข้อมูลต่างชนิดกันเอามารวมไว้ด้วยกันและตั้งชื่อเพื่อใช้เรียก

ไวยากรณ์

struct structure_name {
    type_1 member_1;
    type_2 member_2;
    ...
    type_n member_n;
};


ตัวอย่าง

struct human {
    char name[255];
    int age;
};

เวลาประกาศตัวแปรก็ประกาศเหมือนตัวแปรแบบปกติ

human a;

เวลาเรียกใช้เราจะใช้ . (จุด) เพื่อบอกข้อมูล member ของ structure นั้น

เช่น ถ้าเราจะเข้าถึงข้อมูลที่ชื่อ name ใน a เราจะใช้ a.name

a.name = "Name";
a.age = 5;

แล้วถ้าต้องการตัวแปร human หลาย ๆ ตัว เราก็สามารถสร้างเป็น array ได้

human b[20];

คือการประกาศตัวแปร array ของ human 20 ตัว

เมื่อเราสร้างตัวแปร structure ที่เป็น pointer

human *c = new human;

แล้วถ้าเราต้องการจะเข้าถึง member ของ c จะต้องเขียนยังไง ?

c.age = 5;

หรือ

*c.age = 5;

ทั้งสองแบบข้างบนเป็นวิธีที่ผิด
เราจะต้องนำค่าของ c ออกมาก่อนโดยการใช้ *c แล้วค่อยเข้าถึง member เราจึงต้องใส่ ( ) ครอบ *c

(*c).age = 5;

แต่ถ้าจะพิมพ์วงเล็บ แล้วใส่ * อีก ทำให้เสียเวลาพิมพ์มาก จึงมีสัญลักษณ์ตัวหนึ่งที่ใช้ลดรูป (*_) คือ ->
เราจึงสามารถเปลี่ยนได้เป็น

c->age = 5;

นอกจากนี้เรายังสามารถประกาศตัวแปรหลังจากประกาศ structure ได้เลย โดยการเขียนชื่อตัวแปรต่อท้ายก่อนเครื่องหมาย ;

struct human {
    char name[255];
    int age;
} a, b, c;

คือการประกาศตัวแปร a, b, c เป็น human

2011/10/02

C++ - Pointer to Array

สวัสดีรอบสองของวันนี้ครับ
คราวนี้ผมจะมาเขียนเรื่องการใช้ Pointer เป็น Array

ปกติในภาษา C เวลาจะประกาศตัวแปรแบบ array เราจะประกาศเป็น

int a[5];


คือการประกาศตัวแปร a เป็น array ของ int มีสมาชิก 5 ตัว (0 - 4)

และเวลาที่เราจะประกาศ Pointer เราจะเขียนเป็น

int *b;

คือให้ b เป็น pointer ของ int
ในภาษา C ถ้าเราต้องการให้ pointer เป็น array เราจะต้องจองหน่วยความจำให้มันโดยใช้ฟังก์ชัน calloc


void* calloc(size_t num, size_t size);


ถ้าจะให้ b เป็น array ที่มีสมาชิก 5 ตัวเราจะเขียนว่า

b = (int*)calloc(5, sizeof(int));

และจะใช้


void free(void*);

ในการลบข้อมูลที่จองออก

free(b)

ในภาษา C++ เราสามารถเขียนอีกแบบหนึ่งได้

เวลาจะประกาศตัวแปร int แต่ต้องการให้เป็น pointer เราจะใช้คำสั่ง new ในการจองหน่วยความจำ

int *c = new int;

และเราจะใช้ คำสั่ง delete ลบค่าออก

delete c;

แต่ถ้าต้องการให้ตัวแปรเป็นแบบ array เราจะใส่จำนวนสมาชิกลงไปด้วย

int *d = new int[5];

คือการประกาศให้ d เป็น array ที่มีสมาชิก 5 ตัว
เห็นไหมครับว่าง่ายกว่าภาษา C เยอะเลย

เวลาจะลบข้อมูลเราก็จะใช้

delete [] d;


แล้ว array สองมิติ จะสร้างยังไงดี ?

int **e = new int*[5];
for (int i = 0; i < 5; i++)
e[i] = new int[3];

คือการประกาศให้ e เป็น array ขนาด 5x3
จากตัวอย่างเราจะสังเกตได้ว่า เราไม่จำเป็นที่จะต้องประกาศ array เป็นสี่เหลี่ยมเสมอไป
เราอาจจะประกาศเป็นรูปอะไรก็ได้

int **f = new int*[3];
f[0] = new int[2];
f[1] = new int[3];
f[2] = new int;