簡體   English   中英

這段混淆的 Haskell 代碼是如何工作的?

[英]How does this piece of obfuscated Haskell code work?

在閱讀https://en.uncyclopedia.co/wiki/Haskell (並忽略所有“令人反感”的東西)時,我偶然發現了以下一段混淆代碼:

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

當我在ghci運行那段代碼時(在導入Data.FunctionControl.Applicative ), ghci打印出 2 的所有冪的列表。

這段代碼是如何工作的?

首先,我們有一個可愛的定義

x = 1 : map (2*) x

如果您以前從未見過它,這本身就有點令人費解。 無論如何,這是一個相當標准的懶惰和遞歸技巧。 現在,我們將使用fix和 point-free-ify 擺脫顯式遞歸。

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

我們要做的下一件事是擴展:部分並使map變得不必要地復雜。

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

好吧,現在我們有這個常量1兩個副本。 那永遠不會,所以我們將使用 reader applicative 去重復它。 此外,函數組合有點垃圾,所以讓我們盡可能用(<$>)替換它。

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

接下來:對map調用太易讀了。 但是沒有什么可害怕的:我們可以使用單子定律來擴展它。 特別是, fmap fx = x >>= return . f fmap fx = 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)

正在寫一個很長的答案,其中包含導致最終代碼的實驗的完整運行 IRC 日志(這是在 2008 年初),但我不小心寫下了所有文本:) 雖然損失不大 - 對於Daniel 的大部分分析都恰到好處。

這是我的開始:

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右,直到最后的版本,在這里我在擠(*2)並改變了$2處最后變成$1 “使地圖不必要地復雜”這一步沒有發生(那么早)。
  • 我用liftM2代替liftA2
  • 在用 Applicative 組合器替換 LiftM2 之前,引入了混淆的map函數。 這也是所有空間消失的時候。
  • 甚至我的“最終”版本也有很多. 用於剩余的函數組合。 <$>替換所有這些顯然發生在那個和 uncyclopedia 之間的幾個月里。

順便說一句,這是一個不再提及數字2的更新版本:

fix$(<$>)<$>(:)<*>((<$>((:[{- Jörð -}])<$>))(=<<)<$>(*)<$>(>>=)(+)($))$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 ABC x並且因為liftA2 ABC被應用到x它是一個函數,作為一個函數它意味着liftA2 ABC x = A (B x) (C x)
  • (f `op` g) = op fg = (f `op`) g = (`op` g) f是算子部分的三個定律
  • >>=(`op` g) f = op fg綁定,因為(`op` g) f = op fg並且類型是

    (>>=) :: Monad m => ma -> (a -> mb ) -> mb (\\ 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 (gx) fix f = let x = fx in x iterate fx = x : iterate f (fx) = x : let y = fx in y : iterate f (fy) = x : let y = fx in y : let z = fy in z : iterate f (fz) = ... = [ (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..]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM