โค้ด Haskell ที่สับสนนี้ทำงานอย่างไร


90

ในขณะที่อ่านhttps://en.uncyclopedia.co/wiki/Haskell (และเพิกเฉยต่อสิ่งที่ "น่ารังเกียจ" ทั้งหมด) ฉันสะดุดกับโค้ดที่สับสนต่อไปนี้:

fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1

เมื่อฉันรันโค้ดส่วนนั้นในghci(หลังจากนำเข้าData.FunctionและControl.Applicative) ให้ghciพิมพ์รายการพาวเวอร์ทั้งหมดของ 2

โค้ดชิ้นนี้ทำงานอย่างไร?


3
ฉันสงสัยว่าคำตอบจะเป็นสิ่งที่น่ารังเกียจอย่างยิ่งยวด ...
Meredith

31
คุณได้ลองทำอะไรบ้าง? สิ่งที่ต้องลองอย่างชัดเจนคือ (a) ลบความคิดเห็น (b) จัดรูปแบบใหม่ / reindent โค้ด (c) หาว่าอินสแตนซ์ใดของ Functor / Applicative / Monad กำลังถูกใช้ (อาจเป็นรายการทั้งหมด แต่อย่าถือว่า .. ไม่มีอะไรที่จะป้องกันไม่ให้โปรแกรมเมอร์ที่มีความบกพร่องเพียงพอจากการใช้ Monad ห้าอินสแตนซ์ที่แตกต่างกันในโค้ดบรรทัดเดียว) (d) ทำให้ง่ายขึ้นเท่าที่จะทำได้ แล้วดูว่าคุณเหลืออะไรบ้าง
dave4420

10
Haskell เป็นภาษาโปรแกรมโปรดของฉัน แต่ถึงกระนั้นuncyclopedia.wikia.com/wiki/Haskellทำให้ฉันหัวเราะมาก!
AndrewC


5
มันทำให้ฉันรำคาญมากเมื่อมีคนพบส่วนของรหัสที่เป็นความลับอย่างไร้เหตุผลที่สุดที่พวกเขาสามารถพบได้ในภาษา XYZ จากนั้นก็ยืนยันว่ามันเป็น "แทบเป็นไปไม่ได้เลยที่จะเขียนโค้ดที่อ่านได้ในภาษา XYZ" แต่นั่นเป็นแค่ฉัน ...
MathematicalOrchid

คำตอบ:


218

เริ่มต้นด้วยคำจำกัดความที่น่ารัก

x = 1 : map (2*) x

ซึ่งโดยตัวของมันเองนั้นเป็นเรื่องที่น่าเหลือเชื่อหากคุณไม่เคยเห็นมาก่อน อย่างไรก็ตามมันเป็นเคล็ดลับที่ค่อนข้างเป็นมาตรฐานของความเกียจคร้านและการเรียกซ้ำ ตอนนี้เราจะกำจัดการเรียกซ้ำอย่างชัดเจนโดยใช้fixและแบบไม่มีจุด

x = fix (\vs -> 1 : map (2*) vs)
x = fix ((1:) . map (2*))

สิ่งต่อไปที่เราจะทำคือขยาย:ส่วนและทำให้mapซับซ้อนโดยไม่จำเป็น

x = fix ((:) 1 . (map . (*) . (*2)) 1)

1ดีตอนนี้เรามีสองฉบับอย่างต่อเนื่องว่า นั่นจะไม่เกิดขึ้นดังนั้นเราจะใช้แอพพลิเคชั่นผู้อ่านเพื่อยกเลิกการทำซ้ำ นอกจากนี้องค์ประกอบของฟังก์ชันยังเป็นขยะเล็กน้อยดังนั้นให้แทนที่ด้วย(<$>)ทุกที่ที่ทำได้

x = fix (liftA2 (.) (:) (map . (*) . (*2)) 1)
x = fix (((.) <$> (:) <*> (map . (*) . (*2))) 1)
x = fix (((<$>) <$> (:) <*> (map <$> (*) <$> (*2))) 1)

ถัดไป: คำเรียกmapนั้นอ่านได้มากเกินไป แต่ไม่มีอะไรต้องกลัวเราสามารถใช้กฎหมาย monad เพื่อขยายความได้เล็กน้อย โดยเฉพาะอย่างยิ่งfmap f x = x >>= return . fดังนั้น

map f x = x >>= return . f
map f x = ((:[]) <$> f) =<< x

เราสามารถชี้ได้โดยไม่ต้องมีค่าใช้จ่ายแทนที่(.)ด้วย(<$>)แล้วเพิ่มส่วนปลอมบางส่วน:

map = (=<<) . ((:[]) <$>)
map = (=<<) <$> ((:[]) <$>)
map = (<$> ((:[]) <$>)) (=<<)

การแทนที่สมการนี้ในขั้นตอนก่อนหน้า:

x = fix (((<$>) <$> (:) <*> ((<$> ((:[]) <$>)) (=<<) <$> (*) <$> (*2))) 1)

สุดท้ายคุณทำลายสเปซบาร์ของคุณและสร้างสมการสุดท้ายที่ยอดเยี่ยม

x=fix(((<$>)<$>(:)<*>((<$>((:[])<$>))(=<<)<$>(*)<$>(*2)))1)

4
คุณออกไป{- thor's mother -}!
Simon Shine

13

กำลังเขียนคำตอบยาว ๆ พร้อมกับบันทึก IRC ของการทดลองทั้งหมดที่นำไปสู่รหัสสุดท้าย (นี่คือต้นปี 2008) แต่ฉันบังเอิญข้อความทั้งหมด :) ไม่ได้สูญเสียมากนัก - สำหรับ การวิเคราะห์ของแดเนียลส่วนใหญ่เป็นประเด็น

นี่คือสิ่งที่ฉันเริ่มต้นด้วย:

Jan 25 23:47:23 <olsner>        @pl let q = 2 : map (2*) q in q
Jan 25 23:47:23 <lambdabot>     fix ((2 :) . map (2 *))

ความแตกต่างส่วนใหญ่มาจากลำดับที่การปรับโครงสร้างเกิดขึ้น

  • แทนที่x = 1 : map (2*) xฉันจะเริ่มด้วย2 : map ...และฉันเก็บ 2 เริ่มต้นนั้นไว้จนถึงเวอร์ชันสุดท้ายโดยที่ฉันบีบ a (*2)และเปลี่ยน$2ท้ายเป็น$1. ขั้นตอน "ทำให้แผนที่ซับซ้อนโดยไม่จำเป็น" ไม่ได้เกิดขึ้น (เร็วขนาดนั้น)
  • ฉันใช้ liftM2 แทน liftA2
  • มีmapการใส่ฟังก์ชันที่ทำให้ยุ่งเหยิงก่อนที่จะเปลี่ยน liftM2 ด้วยตัวผสมที่ใช้งานได้ นั่นก็คือเมื่อช่องว่างทั้งหมดหายไป
  • แม้แต่เวอร์ชัน "สุดท้าย" ของฉันก็มี.องค์ประกอบของฟังก์ชันเหลืออยู่มากมาย <$>เห็นได้ชัดว่าการแทนที่สิ่งเหล่านั้นทั้งหมดเกิดขึ้นในช่วงหลายเดือนระหว่างนั้นกับ uncyclopedia

BTW นี่คือเวอร์ชันอัปเดตที่ไม่ได้กล่าวถึงหมายเลขอีกต่อไป2:

fix$(<$>)<$>(:)<*>((<$>((:[{- Jörð -}])<$>))(=<<)<$>(*)<$>(>>=)(+)($))$1

10
การละเว้นคำว่า "ลบ" ในวรรคแรกเป็นการเจตนาหรือไม่? ถ้าเป็นเช่นนั้นฉันก็สวมหมวกให้คุณครับ
Jake Brownson

3
@JakeBrownson มันเป็นสำนวนทางอินเทอร์เน็ตทั่วไปแต่ฉันก็ไม่แน่ใจเหมือนกันว่ามันตั้งใจหรือเปล่า
Lambda Fairy

1

คำตอบทั้งสองได้มาจากข้อมูลโค้ดที่คลุมเครือจากต้นฉบับสั้น ๆ ที่ให้มาจากสีน้ำเงิน แต่จริงๆแล้วคำถามจะถามว่าโค้ดที่คลุมเครือแบบยาวทำงานอย่างไร

วิธีการมีดังนี้

fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1 
= {- add spaces, remove comment -}
fix $ (<$>) <$> (:) <*> ( (<$> ((:[]) <$>) ) (=<<)  <$>  (*)  <$>  (*2) ) $ 1 
--                      \__\______________/_____________________________/
= {-    A   <$> B   <*> C                          $ x   =   A (B x) (C x) -}
fix $ (<$>) (1 :)     ( ( (<$> ((:[]) <$>) ) (=<<)  <$>  (*)  <$>  (*2) ) 1 )
--                      \__\______________/____________________________/
= {- op f g = (f `op` g) ; (`op` g) f = (f `op` g) -}
fix $ (1 :) <$>  ( (((=<<) <$> ((:[]) <$>) )        <$>  (*)  <$>  (*2) ) 1 )
--                  \\____________________/____________________________/
= {- <$> is left associative anyway -}
fix $ (1 :) <$>  ( ( (=<<) <$> ((:[]) <$>)          <$>  (*)  <$>  (*2) ) 1 )
--                  \__________________________________________________/
= {- A <$> foo = A . foo when foo is a function -}
fix $ (1 :) <$>  ( ( (=<<) <$> ((:[]) <$>)           .   (*)   .   (*2) ) 1 )
--                  \__________________________________________________/
= {- ((:[]) <$>) = (<$>) (:[]) = fmap (:[])  is a function -}
fix $ (1 :) <$>  ( ( (=<<)  .  ((:[]) <$>)           .   (*)   .   (*2) ) 1 )
--                  \__________________________________________________/
= {- (a . b . c . d) x = a (b (c (d x))) -}
fix $ (1 :) <$>      (=<<)  (  ((:[]) <$>)           (   (*)   (   (*2)   1 )))
= {- (`op` y) x = (x `op` y) -}
fix $ (1 :) <$>      (=<<)  (  ((:[]) <$>)           (   (*)   2             ))
= {- op x = (x `op`) -}
fix $ (1 :) <$>      (=<<)  (  ((:[]) <$>)              (2*)                  )
= {-  (f `op`) g = (f `op` g) -}
fix $ (1 :) <$>      (=<<)  (   (:[]) <$>               (2*)                  )
= {-  A <$> foo = A . foo when foo is a function -}
fix $ (1 :) <$>      (=<<)  (   (:[])  .                (2*)                  )
= {-  (f . g) = (\ x -> f (g x)) -}
fix $ (1 :) <$>      (=<<)  (\ x -> [2*x]  )
= {- op f = (f `op`)  -}
fix $ (1 :) <$>           ( (\ x -> [2*x]  )  =<<)

นี่( (\ x -> [2*x]) =<<) = (>>= (\ x -> [2*x])) = concatMap (\ x -> [2*x]) = map (2*)คือฟังก์ชั่นอีกครั้ง<$> = .:

= 
fix $ (1 :)  .  map (2*)
= {- substitute the definition of fix -}
let xs = (1 :) . map (2*) $ xs in xs
=
let xs = 1 : [ 2*x | x <- xs] in xs
= {- xs = 1 : ys -}
let ys =     [ 2*x | x <- 1:ys] in 1:ys
= {- ys = 2 : zs -}
let zs =     [ 2*x | x <- 2:zs] in 1:2:zs
= {- zs = 4 : ws -}
let ws =     [ 2*x | x <- 4:ws] in 1:2:4:ws
=
iterate (2*) 1
= 
[2^n | n <- [0..]]

ล้วนเป็นพลังของ2ตามลำดับที่เพิ่มขึ้น


สิ่งนี้ใช้

  • A <$> B <*> C $ x = liftA2 A B C xและตั้งแต่liftA2 A B Cถูกนำไปใช้มันเป็นฟังก์ชั่นเป็นฟังก์ชั่นมันหมายถึงxliftA2 A B C x = A (B x) (C x)
  • (f `op` g) = op f g = (f `op`) g = (`op` g) f เป็นกฎหมายสามส่วนของส่วนการดำเนินการ
  • >>=คือการผูกแบบ monadic และตั้งแต่(`op` g) f = op f gและประเภทคือ

    (>>=)                :: Monad m => m a -> (a -> m b ) -> m b
    (\ x -> [2*x])       :: Num t   =>         t -> [ t]
    (>>= (\ x -> [2*x])) :: Num t   => [ t]               -> [ t]
    

    ตามประเภทการใช้งานและการแทนที่เราจะเห็นว่า monad ที่เป็นปัญหานั้นมี[]ไว้เพื่อ(>>= g) = concatMap gอะไร

  • concatMap (\ x -> [2*x]) xs ถูกทำให้ง่ายขึ้นเป็น

    concat $ map (\ x -> [2*x]) 
    =
    concat $ [ [2*x] | x <- xs]
    =
             [  2*x  | x <- xs]
    =
             map (\ x ->  2*x )
    
  • และตามความหมาย

    (f . g) x  =  f (g x)
    
    fix f  =  let x = f x in x
    
    iterate f x  =  x : iterate f (f x)
                 =  x : let y = f x in 
                        y : iterate f (f y)
                 =  x : let y = f x in 
                        y : let z = f y in 
                            z : iterate f (f z)
                 = ...
                 = [ (f^n) x | n <- [0..]]
    

    ที่ไหน

            f^n  =  f  .  f  .  ...  . f
            --     \_____n_times _______/
    

    ดังนั้น

    ((2*)^n) 1  =  ((2*) . (2*) .  ...  . (2*)) 1
                =    2*  (  2*  (  ...  (  2*   1 )...)) 
                =    2^n   ,  for n in [0..]
    
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.