有人可以解释为什么第一个函数生成流"a"
,"aa"
,"aaa"
..?
或者为什么第二个生成前缀流,例如:
prefixes [1,2,3..]
- > [[],[1],[1,2], [1,2,3]..]
strings = "" : ( map ('a' :) strings )
prefixes (x : xs) = [] : ( map (x :) (prefixes xs) )
答案 0 :(得分:7)
这些都是非常有趣的"打结"例子。您可以通过仔细跟踪评估来理解它们,确保不会评估太多。你也可以等同地理解它们。有时后者更容易
例如,如果我们考虑strings
strungs = map ('a':) strungs
我们可以认为答案是strungs
如果map ('a':)
超过它,那么它就会保持不变。如果我们想象strungs
是一个字符串列表(并且必须是类型),那么对该列表执行map ('a':)
会添加一个新的' a'在每个元素的前面。
因此,strungs
在这样的地图之后没有变化的唯一选择是它是否是任何长度的列表,其中每个元素是无限字符串" aaaaa ..."
现在strings
与strungs
非常相似,但它有另一个条件。无论strings
是什么,它都必须与"" : map ('a':) strings
相同。我们很容易看到我们在这里玩strungs
类似的游戏,除了我们不断添加新的空元素。因此,strings
必须看起来像
"", "a", "aa", "aaa", "aaaa", ...
因为map ('a':) strings
看起来像
"a", "aa", "aaa", "aaaa", "aaaaa", ...
然后预先""
使其与我们开始时的相同
"", "a", "aa", "aaa", "aaaa", "aaaaa", ...
答案 1 :(得分:5)
他们都非常相似,所以我只会告诉你strings
并让你自己找出prefixes
作为练习。
Haskell喜欢既递归又懒惰。懒惰意味着值由 thunks 或未来计算的承诺表示。在评估某些内容时,您可以在GHCi中实际看到它们:
> let strings = "" : map ('a' :) strings
> :print strings
strings = (_t1 :: [[Char]])
> strings !! 0
""
> :print strings
strings = "" : (_t2 :: [[Char]])
> strings !! 1
"a"
> :print strings
strings = "" : "a" :: (_t3 :: [[Char]])
> strings !! 2
"aa"
:print strings
strings = "" : "a" : "aa" : (_t4 :: [[Char]])
每个_tN
代表一个指向尚未评估的流的其余部分的指针。您也可以将其可视化,其中_
表示指向其余strings
的指针
strings
= "" : map ('a':) strings
^--------<------^
= "" : map ('a':) ("" : _)
^--------<-------^
= "" : ('a':"") : map ('a':) (_)
^--------<-------------^
= "" : "a" : map ('a':) ("a" : _)
^-------<---------^
= "" : "a" : ('a':"a") : map ('a':) (_)
^---------<-------------^
= "" : "a" : "aa" : map ('a':) ("aa" : _)
^--------<---------^
= "" : "a" : "aa" : ('a':"aa") : map ('a':) (_)
^----------<-------------^
= "" : "a" : "aa" : "aaa" : map ('a':) ("aaa" : _)
^---------<---------^
= "" : "a" : "aa" : "aaa" : ('a':"aaa") : map ('a':) (_)
^------------<------------^
= "" : "a" : "aa" : "aaa" : "aaaa" : map ('a':) ("aaaa" : _)
^---------<----------^
= ...
希望这是有道理的,每个^-<-^
显示_
在每次迭代中指向的内容(大致)