2

我是 Haskell 的新手,我正在为我的编程语言课写一篇关于它的论文。我想用一些示例代码来展示 Haskell 的懒惰,但我不确定我所看到的是否真的是懒惰。

doubleMe xs = [x*2 | x <- xs]

在 ghci 中:

let xs = [1..10]
import Debug.Trace
trace (show lst) doubleMe (trace (show lst) doubleMe (trace (show lst) doubleMe(lst)))

输出:

[1,2,3,4,5,6,7,8,9,10]
[1,2,3,4,5,6,7,8,9,10]
[1,2,3,4,5,6,7,8,9,10]
[8,16,24,32,40,48,56,64,72,80]

感谢您的时间和帮助!

4

4 回答 4

7

您对trace此处的使用并不是特别有见地,或者实际上根本没有。您所做的只是在评估的四个不同点打印出相同的列表,这并不能告诉您有关程序实际状态的任何信息。这里实际发生的是trace在计算甚至开始之前在每个加倍步骤中强制执行(当结果列表被请求为弱头范式时)。这与使用完全严格评估的语言几乎相同。

要看到一些懒惰,你可以做类似的事情

Prelude Debug.Trace> let doubleLsTracing xs = [trace("{Now doubling "++show x++"}")$ x*2 | x<-xs]
Prelude Debug.Trace> take 5 $ doubleLsTracing [1 .. 10]
{Now doubling 1}
{Now doubling 2}
{Now doubling 3}
{Now doubling 4}
{Now doubling 5}
[2,4,6,8,10]

您可以看到只有五个数字加倍,因为只请求了五个结果;即使doubleLsTracing给出的列表有 10 个条目。

请注意,这trace通常不是监视“执行流程”的好工具,它只是一种允许“查看”局部变量以查看某些函数中发生了什么的技巧。

于 2012-12-10T20:55:53.333 回答
5

无限流总是一个很好的例子。如果没有特殊的结构,你无法在其他语言中获得它们——但在 Haskell 中它们是非常自然的。

一个例子是斐波那契流:

fib = 0 : 1 : zipWith (+) fib (tail fib)

take 10 fib => [0,1,1,2,3,5,8,13,21,34]

另一种是通过试除法获得素数流:

primes = sieve [2..]
    where sieve (x:xs) = x : filter (not . (== 0) . (`mod` x)) (sieve xs)

take 10 primes => [2,3,5,7,11,13,17,19,23,29]

此外,在 Haskell 中实现回溯非常简单,让您能够按需懒惰地获取解决方案列表:

http://rosettacode.org/wiki/N-queens_problem#Haskell

一个更复杂的例子,展示了如何实现 min 是在这里找到的:

惰性评估和时间复杂度

它基本上展示了如何使用 Haskell 的惰性来获得minimum函数的非常优雅的定义(在元素列表中找到最小值):

minimum = head . sort

你可以通过人为的例子来证明 Haskell 的懒惰。但我认为展示惰性如何帮助您为常见问题开发解决方案要好得多,这些解决方案表现出比其他语言更大的模块化。

于 2012-12-11T00:25:29.330 回答
2

最简洁的答案是不”。leftaroundabout 在他的回答中很好地解释了这一点。

我的建议是:

  1. 阅读并理解惰性求值的定义。
  2. 编写一个函数,其中一个参数可以发散,这个例子不能在你最喜欢的严格(非惰性)语言(C、python、Java)中工作。例如sumIfFirstArgIsNonZero (x, y),如果 x != 0 则返回 x+y,否则返回 0。
  3. 对于加分,定义你自己的函数ifThenElse不使用 Haskell 的内置 if-then-else 语法,并解释为什么用惰性语言编写新的控制流结构很容易。

这应该比试图围绕无限数据流或打结技巧更容易。

于 2012-12-10T21:12:56.407 回答
2

懒惰的要点是不需要计算的值 - 所以为了证明这一点,你必须显示没有被评估的东西。您的示例并不是展示惰性的最佳方式,因为最终会计算所有值。

这是一个小例子,作为一个起点:

someValueThatNeverTerminates = undefined -- for example, a divide-by-zero error

main = do (greeting, _) = ("Hello terminating world!", someValueThatNeverTerminates)
          putStrLn greeting

这立即打招呼——如果它不懒惰,整个事情就会中途中断。

于 2012-12-11T03:00:03.117 回答