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

No comments: