在Haskell中展示懒惰评估?

时间:2016-01-20 13:16:21

标签: haskell lazy-evaluation

这是我的一个家庭作业中的一个问题,我无法回答。它是通过演示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)))) =
...

我想帮助理解懒惰的评估是如何运作的,所以我可以做这个问题。

1 个答案:

答案 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]evensexchange的不必要匹配,它要求输入中的三个元素,以查看尾部是否为空( [])或不。

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