Lazy evaluation ใน Haskell

  • warning: realpath() [function.realpath]: SAFE MODE Restriction in effect. The script whose uid is 1005 is not allowed to access /tmp owned by uid 0 in /var/www/sites/sugree/codenone.com/subdomains/www/html/includes/file.inc on line 190.
  • warning: realpath() [function.realpath]: SAFE MODE Restriction in effect. The script whose uid is 1005 is not allowed to access /tmp owned by uid 0 in /var/www/sites/sugree/codenone.com/subdomains/www/html/includes/file.inc on line 190.

คือผมเขียน ML มาได้สักพัก ชอบใจเพราะว่าเป็นภาษาที่มี static typing ที่แทบไม่ต้องบอก type (มันจะหาให้เอง) ทีนี้มีอีกภาษาหนึ่ง ซึ่งคล้ายกันมาก คือ Haskell สองภาษานี้คล้ายกันมาก (ลองดูโปรแกรมที่ http://www.codenone.com/node/22 ) มีเหมือน ๆ กันหมด ทั้งเป็น Functional มี pattern matching อะไรพวกนี้

พอเห็นที่นี่มีคนใช้หลายคน ก็เลยอยากลองเล่นดูบ้่าง แต่ เอ๊ะ ทำไมมันคล้ายกันจัง... เลยไปลอง ๆ หาว่าอืม มันต่างกันตรงไหน

ประเด็นของความแตกต่างคือเรื่องที่ว่า Haskell เป็นภาษาแบบ Lazy evaluation ซึ่งเรามักจะไม่ค่อยเห็นกันในภาษาปกติ กล่าวคือ ในการเรียก function ของภาษาอื่น ๆ (C, Pascal, Java รวมถึง ML ด้วย) argument ต่าง ๆ ของฟังก์ชันจะถูก evaluate ก่อนที่จะเรียกฟังก์ชัน ลักษณะแบบนี้เรียกว่า eagar evaluation

ตัวอย่างที่เห็นได้ชัดก็คือ สมมติเรามีฟังก์ชัน
toInfinity i = i : toInfinity (i+1)

ซึ่งเป็นฟังก์ชันที่เรียกตัวเองไม่รู้จบ สร้าง list ที่มีสมาชิกเพิ่มจาก i ไปเรื่อย ๆ ฟังก์ชันนี้ ถ้าเป็นภาษาอื่น ๆ ฟังก์ชันนี้คงเอาไปใช้ประโยชน์อะไรไม่ได้ เพราะว่าเรียกปุ๊บ เน่าทันใด

อย่างไรก็ตาม ในภาษาที่เป็น lazy evaluation นั้น argument ของฟังก์ชันที่ถูกส่งไป จะไม่ถูก evaluate จนกว่าจะจำเป็น ยกตัวอย่างเช่นถ้าเรามีฟังก์ชัน first ที่ตัดหัวลิสต์มา (เพิ่งลองเล่น ไม่รู้ haskell มีฟังก์ชันประมาณนี้หรือเปล่า) ที่เขียนได้เป็น

first [] _     = []
first _ 0      = []
first (x:xs) i = x:first xs (i-1)

สังเกตว่าฟังก์ชันนี้จะใช้แค่ i ตัวแรกของลิสต์เท่านั้น ดังนั้นถ้าเราเรียก

first (toInfinity 0) 10  -- ได้ผลลัพธ์เป็น [0,1,2,3,4,5,6,7,8,9]
first (toInfinity 100) 5 -- ได้ผลลัพธ์เป็น [100,101,102,103,104]

ลองเขียนแบบนี้ในภาษาอื่น ๆ คงจะ run ไม่ออกเพราะว่ามัวแต่ evaluate toInfinity อยู่

ใน Haskell มันทำ lazy evaluation ให้เป็นปกติเลย ส่วนในภาษาอื่น ๆ เช่น ML เท่าที่ลองกด ๆ ดูเห็นว่ามีวิธีทำได้ รวมถึง ใน Python, และ Ruby ด้วย (ดู ของ ruby และ ของ python)

เห็นบางคนก็ใช้ศัพท์ว่า
call-by-need สำหรับพวก lazy evaluation
และ call-by-value สำหรับพวก eager evaluation

-- ถ้า
plus a b = (+) a b
mul  a b = (*) a b

-- แล้ว
plus (mul (plus 1 2) (plus 7 2)) (mul 2 4))

คำถามก็คือ plus 1 2, plus 7 2 หรือ mul 2 4 อันไหน จะถูกเรียกใช้ก่อนกัน
คือถ้าเป็น eager evaluation, plus 1 2 ต้องถูก evaluate ก่อนแน่นอน
(เดาเอานะ มันน่าจะทำ left ก่อนแล้วค่อยไป right)
แต่ haskell ไม่ได้รับประกันลำดับในการ evaluate
เราจึงไม่รู้แน่ว่า plus 1 2 หรือ mul 2 4 อันไหนถูกเรียกใช้ก่อน



ปัญหาก็คือลำดับการ evaluate มีผลอย่างมาก
เวลาที่เราติดต่อกับ IO หรือพวก concurrency
haskell จึงแก้ปัญหาพวกนี้โดยใช้ Monad

สำหรับผม อุปสรรคในการเรียกรู้ haskell ก็คือเจ้า monad นี่แหล่ะ
อ่านมานานแล้ว มันซืมเข้าไปนิดเดียว

(ตอนนี้มีความหวังใหม่แล้ว รออาจารย์มะนาว)

อะจ๊าด.... กดเข้าไปอ่านในวิกิพีเดีย ... หมดแรงเลยครับ
เดี๋ยวเราค่อย ๆ แปะตัวอย่าง ยั่ว ๆ คนอื่น ๆ ไปก่อนดีไหมครับ เผื่อมีคนพลังเยอะมาเรียนแล้วเอามาสอน อิอิ

freeman's picture

รอคนบ้าพลังมาสอนเหมือนกัน

พลังเยอะ กับบ้าพลัง นี่คงไม่ค่อยเหมือนกันนะครับ

ย้าย Codenone

ประกาศย้าย Codenone ไปใช้ Forum ของ Blognone แทนครับ ตามไปตั้งกระทู้ต่อได้ที่ Codenone Forum (รายละเอียดอ่านจากกระทู้ ย้าย Codenone ไปรวมกับ Blognone)

กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น