Thursday, February 09, 2006

ลอก list -> ruby (ต่อ)

ต่อจากเมื่อวาน ที่ลอก Permutation จาก lisp -> ruby
วันนี้ลองลอก function ที่ชื่อ make-perm บ้าง

Note: การลอกแบบนี้ เป็นการยิงปืนทีเดียวได้นก 2 ตัว
เพราะช่วยทำให้เราเข้าใจทั้ง ruby และ lisp ได้ดีขึ้น
วิธีการเรียนโดยการลอกนี้
ในพวกที่เรียกวิชาจิตรกรรม ก็มีการใช้เหมือนกัน
โดยให้นักเรียนไปนั่งลอกภาพ master piece ของ จิตรกรชั้นนำต่างๆ

ดูลักษณะการใช้งานของ make-perm ก่อน
irb(main):785:0> p = make_perm [1,2], ['s','m'], ['red','blue']
#
irb(main):786:0> p.call
[1, "s", "red"]
irb(main):787:0> p.call
[1, "s", "blue"]
irb(main):788:0> p.call
[1, "m", "red"]
irb(main):789:0> p.call
[1, "m", "blue"]
irb(main):790:0> p.call
[2, "s", "red"]
irb(main):791:0> p.call
[2, "s", "blue"]
irb(main):792:0> p.call
[2, "m", "red"]
irb(main):793:0> p.call
[2, "m", "blue"]
irb(main):794:0> p.call
nil


ใจความหลักก็คือ สิ่งที่ return กลับมาจาก make_perm ก็คือ lambda
ที่เราสามารถ call เพื่อขอสมาชิกตัวถัดไปได้
(เป็น iterate object แบบหนึ่ง)

ลองดู code

def make_perm(*ls)
return nil if ls.empty?

if ls.length == 1 :
list = ls[0].clone
return lambda {
list.shift
}
else
prm = make_perm(*ls[1..ls.length])
list = ls[0].clone
return lambda {
y = prm.call
if y
[list[0], *y]
else
list.shift
return nil if list.length == 0
prm = make_perm(*ls[1..ls.length])
[list[0], *prm.call]
end
}
end
end


ถ้าใครยังไม่คุ้นกับ return lambda แบบนี้ลองดูตัวอย่าง ง่ายๆ นี้ก่อน

def make_pok(ls)
lambda {
ls.shift
}
end

p = make_pok([1,2,3])
p.call # => 1
p.call # => 2
p.call # => 3


key หลัก ก็คือการ set ตัวแปร local ที่อยู่นอก block lambda
ซึ่ง lambda block จะอ้างถึงตัวแปรเหล่านี้

prm = make_perm(*ls[1..ls.length])
list = ls[0].clone
return lambda {
y = prm.call
...
[list[0], *y]


Note: ที่ใช้ clone เพราะผมใช้วิธี array.shift ซึ่งมันมี effect ต่อ array ต้นฉบับ

สังเกตุว่ามีการใช้ เครื่องหมาย '*'

[list[0], *y]

คือถ้าไม่ใส่ * ผลลัพท์จะได้แบบนี้แทน

[1, ["s", "red"]]

Related link from Roti

No comments: