这是我的一个家庭作业中的一个问题,我无法回答。它是通过演示Haskell编译器(解释器?)如何执行程序来推理Haskell代码的。
我给出了一些不同的功能......
-- built-in
take :: Int -> [a] -> [a]
take 0 _ = []
take n (x:xs) = x : (take (n - 1) xs)
-- exchanging entries
exchange :: [a] -> [a]
exchange [x,y] = [y,x]
exchange (x:y:xs) = (y:x:(exchange xs))
-- picking even numbered entries
evens :: [a] -> [a]
evens [x,y] = [x]
evens (x:_:xs) = x:(evens xs)
-- first four numbers repeated
first_four :: [Int]
first_four = 1:2:3:4:first_four
现在我必须通过"假装成编译器"来证明对懒惰评估的理解。通过细分这个陈述的执行方式......
> take 5 (evens (exchange first_four))
[2,4,2,4,2]
我给出了前几行帮助开始...
take 5 (evens (exchange first_four)) =
take 5 (evens (exchange (1:2:3:4:first_four))) =
take 5 (evens (2:1:(exchange (3:4:first_four)))) =
...
我想帮助理解懒惰的评估是如何运作的,所以我可以做这个问题。
答案 0 :(得分:4)
将您的定义视为重写方程式,并始终(唯一)将临时实体命名为:
take 5 (evens (exchange first_four))
-- match: take 0 .... = .... ? FAIL
-- match: take n (x:xs) = .... ? SUCCESS
n1 = 5
(x1:xs1) ?= evens (exchange first_four)
-- evens [x,y] = ....
[x2,y2] ?= exchange first_four
-- exchange [x,y] = ....
[x3,y3] ?= first_four
......
等。操作是机械的。 "懒惰"这意味着,我们从左到右进行,从不试图过早地找出表达式的值,只有当我们真正需要模式匹配某些定义时才这样做。
这就是"命名" 临时实体的含义:
take 5 (evens (exchange first_four)) =
take 5 xs where xs = evens (exchange first_four)
(x1:xs1) ?= xs -- <---- THIS
= evens (exchange first_four)
= evens ys where ys = exchange first_four
[x2,y2] ?= ys -- <--- AND THIS
. . = exchange first_four
. . -- ^^ <--- already named
. . [x3,y3] ?= first_four
. . FAIL
. . (x3:y3:xs3) ?= first_four
. . = 1:2:3:4:first_four
. . SUCCESS: x3=1
. . y3=2
. . xs3=3:4:first_four
. . ys = y3:x3:exchange xs3 !
. . [] ?= exchange xs3
. = ...
. FAIL
(x2:_:xs2) ?= ys
SUCCESS: x2=y3
xs2=...
当然,为这个精细的细节解决方案工作是非常烦人的,很少需要,也许只是跟踪一些&#34;时间&#34;问题;更容易看出每个定义分别做什么,并将它们视为链中连接的独立生产者。
&#34;计时&#34;我的意思是,&#34;内部&#34;生产者准备在&#34;外部&#34;一个人需要它。因为如果没有,整个过程就会陷入困境,&#34;非生产性&#34; 。这里最内部的生产者是一个无限的流,直接定义,所以没有这样的问题。
所以在精神上,我们可以对自己说:&#34; first_four
是1到4的无限重复流; exchange
交换它获得的每对元素; evens
扔掉奇数位置的每个元素; take
需要n个元素&#34;。
实际上,事实证明你一直都是对的。
由于定义的编写方式,evens
强制推出exchange
个输出元素,以四个为一组,或多或少。主要是,罪魁祸首是[x,y]
和evens
中exchange
的不必要匹配,它要求输入中的三个元素,以查看尾部是否为空( []
)或不。
evens
需要三个元素,但exchange
只知道如何成对生成它们。
但exchange
也要求供应商提供至少三个要素。所以,粗略地说:
take 5 (evens (exchange first_four)) take 5 (evens (exchange first_four)) take 5 (evens (exchange (1:2:3:(4:first_four))) take 5 (evens (2:1:exchange (3:4:1:(2:3:4:first_four)))) take 5 (evens (2:1:4:(3:exchange (1:2:3:4:first_four)))) take 5 (2:evens (4:3:exchange (1:2:3:4:first_four))) 2:take 4 (evens (4:3:exchange (1:2:3:4:first_four))) 2:take 4 (evens (4:3:2:(1:exchange (3:4:first_four)))) 2:take 4 (4:evens (2:1:exchange (3:4:first_four))) 2:4:take 3 (evens (2:1:exchange (3:4:1:(2:3:4:first_four)))) 2:4:take 3 (evens (2:1:4:(3:exchange (1:2:3:4:first_four)))) 2:4:take 3 (2:evens (4:3:exchange (1:2:3:4:first_four))) ........
我相信你现在可以完成这件事了。 :)
为了说明这里,exchange
将程序视为流变换器,它可以pull
来自其输入的元素; peek
其输入是否有准备好的元素;并且push
将一个元素放入其输出中。
exchange:
pull(X) ... on_fail: ERROR
pull(Y) ... on_fail: ERROR
peek ... on_fail: push(Y); push(X); STOP
push(Y); push(X)
LOOP