【问题标题】:Demonstrating Lazy evaluation in Haskell?在 Haskell 中演示惰性评估?
【发布时间】:2016-01-20 13:16:21
【问题描述】:

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

我需要一些帮助来了解惰性评估的工作原理,以便我可以回答这个问题。

【问题讨论】:

  • 在来这里之前,您是否尝试过阅读网上已有的众多资源?
  • @BartekBanachewicz 是的
  • 我曾经在这里为一个 Haskell 函数写了一个详细的评估链:stackoverflow.com/questions/29898999/…也许这会对你有所帮助。
  • 如果你真的应该解释惰性部分,我认为这里的第一个评估步骤不是很诚实......
  • @Carsten 确实,这些行完全是错误的。

标签: haskell lazy-evaluation


【解决方案1】:

将您的定义视为重写方程式,并始终(唯一)在临时实体起作用时为其命名:

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=...

当然,处理这种精细的细节解决方案非常令人厌烦且很少需要,可能只是为了跟踪一些“时间”问题;更容易分别查看每个定义的作用,并将它们视为连接在链中的独立生产者。

我所说的“时间”是指“内部”生产者是否准备好在“外部”生产者需要时生产它的元素。因为如果没有,整个过程就会卡住,“非生产性”。这里最内部的生产者是一个无限流,直接定义,所以不存在这样的问题。

所以在心理上,我们可以对自己说:“first_four 是 1 到 4 的无限重复流;exchange 交换它获得的每一对元素;evens 丢弃所有奇数位置的元素;take取 n 个元素”。

事实上,事实证明你一直都是对的

由于定义的编写方式,evens 强制从exchange 输出元素以四个为一组,或多或少。罪魁祸首主要是[x,y]evensexchange中的不必要匹配,这要求其输入中的三个元素,以查看尾部是否为空([]) .

evens 需要三个元素,但exchange 只知道如何成对生成它们。

但是exchange 也要求其供应商提供至少三个元素。所以,粗略地说:

拿 5 (evens (exchange first_four)) 取5(偶数(交换first_four)) 取 5 (偶数 (交换 (1:2:3:(4:first_four))) 取 5 (偶数 (2:1:exchange (3:4:1:(2:3:4:first_four)))) 取 5 (偶数 (2:1:4:(3:exchange (1:2:3:4:first_four)))) 取 5 (2:evens (4:3:exchange (1:2:3:4:first_four))) 2:取4(偶数(4:3:交换(1:2:3:4:first_four))) 2:取4(偶数(4:3:2:(1:exchange (3:4:first_four)))) 2:take 4 (4:evens (2:1:exchange (3:4:first_four))) 2:4: 取 3 (偶数 (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: 取 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

【讨论】:

  • 虽然我不知道临时实体是什么意思
  • x1xs1 等。强制表达式值的计算是需要在需要时对其进行模式匹配。与take 一样,take 0 ... = ... 与调用 take 5 (...) 不匹配;所以我们继续到take 定义中的下一个子句,并尝试将调用take 5 (...)take n (x:xs) = ... 匹配。所以n 匹配5,通过注册n = 5。接下来,我们必须查看(evens ...) 是否与(x:xs) 匹配(但是,我们将它们重命名为x1:xs1(注意!))。所以我们继续看evens的定义。等等。 - 不要犹豫,问更多。 :)
  • 好的,我想我明白了,我在正确的轨道上吗? ... 取 5 (偶数 (2:1:(exchange (3:4:first_four)))) = take 5 (2:(evens (exchange 3:4:first_four)))
  • 事实证明你一直都是对的。 :) even 需要 3 个元素,但 exchange 只知道如何成对生成它们。
猜你喜欢
  • 1970-01-01
  • 2016-12-14
  • 2014-02-08
  • 2015-09-06
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
相关资源
最近更新 更多