在阅读https://en.uncyclopedia.co/wiki/Haskell(并忽略所有“令人反感”的东西)时,我偶然发现了以下混淆代码:
fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1
当我在ghci
(Data.Function
和导入后Control.Applicative
)中运行该代码时,ghci
输出2的所有幂的列表。
此段代码如何工作?
在阅读https://en.uncyclopedia.co/wiki/Haskell(并忽略所有“令人反感”的东西)时,我偶然发现了以下混淆代码:
fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1
当我在ghci
(Data.Function
和导入后Control.Applicative
)中运行该代码时,ghci
输出2的所有幂的列表。
此段代码如何工作?
Answers:
首先,我们有一个可爱的定义
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)
{- thor's mother -}
!
我写了一个很长的答案,并完整地浏览了我的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一直保留到最后一个版本,在那里我挤了a (*2)
,$2
最后将改为了$1
。那个早期没有发生“使地图不必要地复杂”的步骤。map
在使用Appliative组合器替换liftM2之前,请使用混淆功能。那时所有的空间都消失了。.
功能组成部分。用这些替换所有这些<$>
显然在那和非百科全书之间的几个月中发生了一段时间。顺便说一句,这是一个更新的版本,不再提及数字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 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是[]
针对哪个的(>>= 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..]