在Haskell中演示惰性评估?

在Haskell中演示惰性评估?,haskell,lazy-evaluation,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]

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

我想了解懒惰评估是如何工作的,这样我就可以回答这个问题了

将您的定义视为重新编写的方程式,并始终在过渡实体发挥作用时对其进行唯一命名:

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=...
我相信你现在能完成这件事

举例来说,这里的交换从程序上看,它是一个流转换器,可以从输入中提取元素;查看它的输入是否有一个准备好拉取的元素;并将一个元素推入其输出

exchange:
   pull(X)  ...  on_fail: ERROR
   pull(Y)  ...  on_fail: ERROR
   peek     ...  on_fail: push(Y); push(X); STOP
   push(Y); push(X)
   LOOP

来到这里之前,你有没有阅读过网上已有的大量资源?@BartekBanachewicz YesI曾在这里为Haskell函数写过一个详细的评估链:也许这会对你有所帮助。如果你真的要解释懒惰的部分,我认为这里的第一个评估步骤不是很诚实…@Carsten确实,这些行完全错了。我不知道过渡实体是什么意思,比如x1和xs1,等等。当需要时,强制计算表达式值的是需要模式匹配它。与take一样,take 0…=。。。与呼叫不匹配,需要5。。。;所以我们继续讨论take定义中的下一个子句,并尝试匹配调用take 5。。。取nx:xs=。。。。因此,通过注册n=5,n与5匹配。接下来,我们必须看看埃文斯是否。。。与x:xs匹配,但我们将其重命名为x1:xs1 NB!。所以我们继续看evens的定义。等等不要犹豫,多问一些好的,我想我明白了,我的想法对吗。。。5个晚上2:1:交换3:4:first\u four=5个晚上2:evens交换3:4:first\u four原来你一直都是对的甚至需要3个元素,但exchange只知道如何成对生成它们。
exchange:
   pull(X)  ...  on_fail: ERROR
   pull(Y)  ...  on_fail: ERROR
   peek     ...  on_fail: push(Y); push(X); STOP
   push(Y); push(X)
   LOOP