这段混淆的Haskell代码如何工作?

时间:2012-09-30 08:50:04

标签: haskell

在阅读http://uncyclopedia.wikia.com/wiki/Haskell(并忽略所有“令人反感”的东西)时,我偶然发现了以下一段混淆代码:

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

当我在ghci中运行该段代码时(导入Data.FunctionControl.Applicative后),ghci会打印所有2的幂列表。

这段代码如何运作?

3 个答案:

答案 0 :(得分:211)

首先,我们有一个可爱的定义

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的副本。这将永远不会,所以我们将使用阅读器应用程序去重复它。此外,功能组合有点垃圾,所以我们尽可能用(<$>)替换它。

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)

答案 1 :(得分:14)

我的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,直到最后一个版本,我在(*2)中挤压并更改了$2最后进入$1。 “让地图不必要地复杂化”的步骤没有发生(那个早期)。
  • 我使用liftM2而不是liftA2
  • 在使用Applicative组合器替换liftM2之前,已经使用了混淆的map函数。这也是所有空间消失的时候。
  • 即使是我的“最终”版本也有很多.剩余的功能组合。用<$>替换所有这些显然发生在那个和非百科全书之间的几个月里。

BTW,这是一个更新版本,不再提及数字2

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

答案 2 :(得分:0)

两个答案都是从突然给出的简短原始代码中得出混淆代码段,但问题实际上是询问了长混淆代码是如何工作的。

方法如下:

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被应用于x,因此它是一个函数,而函数的含义是liftA2 A B C x = A (B x) (C x)
  • (f `op` g) = op f g = (f `op`) g = (`op` g) f是运算符部分的三个定律
  • >>=是单原子绑定,并且由于(`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是[]的{​​{1}}。

  • (>>= 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 _______/