Friday, November 21, 2008

เรียนรู้ clojure - data structure ตอน 2

ต่อจากเมื่อวาน
data structure ตัวถัดไปก็คือ map
ลองดูคำสั่งที่เขาใช้ (เขาใช้ map keyboard code เข้ากับ ทิศทาง)
(def AsciiToDx {72 -1,
76 1,
75 0,
74 0})

เจ้าเครื่องหมาย { เป็น syntax อย่างย่อ
ถ้าจะสั่งแบบยาวหน่อย ก็ให้สั่งแบบนี้
(def AsciiToDx (hash-map 72 -1 76 1 75 0 74 0))

เครื่องหมาย comma นั้นจะใส่หรือไม่ใส่ก็ได้
(def AsciiToDx (hash-map 72 -1, 76 1, 75 0, 74 0))

การ access ค่า สามารถทำได้โดยคำสั่ง get
user> (get AsciiToDx 72)
-1


ถัดจาก map ก็คือ vector และ list
ทั้ง vector และ list จะใช้คำสั่งเหมือนกันอย่างกับแกะ ต่างกันตรง data structure ภายในที่เก็บ
เจ้า vector เวลาเรา add item เข้าไป, item นั้นจะไปต่อท้าย
ส่วน list นั้น ถ้าเรา add item เข้าไป, item จะไปอยู่ที่หัว
user> (conj '(1 2 3) 4)
(4 1 2 3)
user> (conj [1 2 3] 4)
[1 2 3 4]

Note: จะเห็นเครื่องหมาย ' ที่หน้า list (1 2 3)
เครื่องหมายนั้นจะบอก clojure ว่า (1 2 3) เป็น data นะ ไม่ต้องไป evaluate มัน

เวลาดูโปรแกรม clisp จะเห็นว่ามีเครื่องหมาย () อยู่เต็มไปหมด (ชื่อภาษามันบอกอยู่แล้วว่ามันคือ list)
แต่เจ้า clojure ที่ลอก syntax มา มีการดัดแปลง syntax ไปเล็กน้อย
โดยจะเห็นว่าเขาเอา vector มาใช้ define พวก parameter list, binding list
ซึ่งก็น่าจะทำให้ programmer recognise code ได้ไวขึ้น

Related link from Roti

Thursday, November 20, 2008

เรียนรู้ clojure - data structure

เมื่อวาน @somkiat post link http://blog.learnr.org/post/59883018/first-clojure-program ใน twiiter
ผมตามไปอ่านแล้ว รู้สึกถูกใจในขนาดและความยากที่ดูจะเหมาะแก่การเริ่มต้นเรียนรู้ Clojure

เริ่มกันที่ data structure ก่อน
ในโปรแกรมนี้เขาใช้ StructMaps ในการเก็บค่าต่างๆเช่น position ของ กำแพง,ผู้เล่น
โดย StructMaps นั้นสามารถสร้างได้โดยใช้คำสั่งู่แบบนี้
(defstruct pos :x :y)

Note: เจ้าคำสั่ง defstruct จริงๆมันเป็นแค่ macro
สิ่งที่เกิดขึ้นจริงๆ ก็คือมันจะแปลงคำสั่ง (defstruct pos :x :y) ให้เป็น (def pos (create-struct :x :y))

เวลาที่เราต้องการสร้าง data ก็ให้สั่งแบบนี้
(struct pos 1 2)

ถ้าต้องการ get ค่า x ก็ให้สั่ง
(:x (struct pos 1 2))

เนื่องจาก Clojure พยายามเน้นที่ Parallel programming
ดังนั้น struct เมื่อสร้างขึ้นมาแล้ว ก็ไม่สามารถเปลี่ยนค่าภายในได้ (immutable)
ถ้าเราต้องการเปลี่ยนค่าภายใน ก็หมายถึงว่าเราต้องสร้าง data ใหม่ขึ้นมา
ดูตัวอย่างนี้
user> (def mypos (struct pos 1 2)) ;; สร้างตัวแปร mypos ที่เก็บตำแหน่ง (1,2)
#=(var user/mypos)
user> (assoc mypos :x 10) ;; เปลี่ยนค่า x ให้เป็น 10
{:x 10, :y 2}
user> (:x mypos) ;; แสดงค่า x
1
user> (def mypos (assoc mypos :x 10)) ;; ถ้าต้องการเปลี่ยนก็หมายถึงว่าต้อง set mypos ให้ชี้ไปที่ StructMap ตัวใหม่ที่ return จาก assoc
#=(var user/mypos)
user> (:x mypos)
10


เจ้า StructMaps จริงๆแล้วก็คือ Map ที่ define keys ที่เป็นไปได้ไว้ (รวมทั้งลำดับด้วย)
เราสามารถใช้ คำสั่งต่างๆที่เกี่ยวกับ Map มาทำ operation บน StructMaps ได้หมด
เช่น
user> (vals mypos) ;; return values ของ mypos
(10 2)
user> (keys mypos) ;; return keys ของ mypos
(:x :y)


คำสั่งที่น่าสนใจ ก็คือ accessor
ที่ return function ที่ใช้ access value ของ StructMaps นั้นๆ
user> (def get-x (accessor pos :x))
#=(var user/get-x)
user> (get-x mypos)
10


ในโปรแกรมจะเห็นตัวอย่างหนึ่งที่น่าสนใจก็คือ
เขาประกาศ struct ของ level ไว้ดังนี้
(defstruct level
:player ; pos
:boxes ; set-of-pos
:goals ; set-of-pos
:walls ; set-of-pos
)

ที่น่าสนใจก็คือ ปกติถ้าเราต้องการสร้าง data ของ level เราก็ใช้คำสั่ง
(struct level a b c d) ได้
แต่การใช้คำสั่งแบบนี้ จะทำให้สับสนภายหลังได้ง่าย ว่า ตัวแปร a,b,c,d หมายถึงอะไร
ดังนั้นวิธีการที่เหมาะก็คือ ใช้คำสั่ง struct-map ในการสร้าง data แทน
(struct-map level
:player (first (filter-level expl-level \@ \+))
:boxes (filter-level expl-level \o \*)
:goals (filter-level expl-level \. \* \+)
:walls (filter-level expl-level \# \#))))

Related link from Roti