懒惰的本质。哈斯克尔

时间:2016-03-07 16:33:02

标签: haskell

我正在学习一个haskell几天,懒惰就像流行语。因为我不熟悉懒惰(我一直主要使用非函数式语言),这对我来说并不容易。

所以,我要求任何表现出懒惰的例外/例子。

提前致谢;)

5 个答案:

答案 0 :(得分:4)

在Haskell中,您可以创建无限列表。例如,所有自然数:

[1,2..]

如果Haskell一次性加载了内存中的所有项目,那是不可能的。要做到这一点,你需要无限的记忆。

懒惰允许您根据需要获取数字。

答案 1 :(得分:3)

这里有一些有趣的东西:动态编程,每个介绍的祸根。算法学生,当用懒惰和功能语言编写时,变成简单自然。以the example of string edit distance为准。这是测量两个DNA链的相似程度或二进制可执行文件的两个版本之间有多少字节变化的问题,或者是如何“不同”的问题。两个字符串。用数学方法表示的动态编程算法很简单:

let:

• d_{i,j} be the edit distance of
  the first string at index i, which has length m
  and the second string at index j, which has length m
• let a_i be the i^th character of the first string
• let b_j be the j^th character of the second string

define:

d_{i,0} = i                   (0 <= i <= m)
d_{0,j} = j                   (0 <= j <= n)
d_{i,j} = d_{i - 1, j - 1}    if a_i == b_j
d_{i,j} = min {               if a_i != b_j
  d_{i - 1, j} + 1 (delete)
  d_{i, j - 1} + 1 (insert)
  d_{i - 1, j - 1} + 1 (modify)
}

return d_{m, n}

用Haskell表示的算法遵循相同的算法形式:

distance a b = d m n
  where (m, n) = (length a, length b)
        a'     = Array.listArray (1, m) a
        b'     = Array.listArray (1, n) b

        d i 0 = i
        d 0 j = j
        d i j
          | a' ! i ==  b' ! j = ds ! (i - 1, j - 1)
          | otherwise = minimum [ ds ! (i - 1, j)     + 1
                                , ds ! (i, j - 1)     + 1
                                , ds ! (i - 1, j - 1) + 1
                                ]

        ds = Array.listArray bounds
               [d i j | (i, j) <- Array.range bounds]
        bounds = ((0, 0), (m, n))

在严格的语言中,我们无法直接定义它,因为数组的单元格将被严格评估。在Haskell中,我们能够让每个单元格的定义引用其他单元格的定义,因为Haskell是惰性的 - 只有当d m n向数组询问最后一个值时,才会在最后评估定义。细胞。一种懒惰的语言可以让我们建立一个站立的多米诺骨牌图表;只有当我们要求一个我们需要计算价值的价值时,它才会推翻第一个多米诺骨牌,推翻所有其他多米诺骨牌。 (在严格的语言中,我们必须设置一个闭包数组,完成Haskell编译器为我们自动完成的工作。它可以很容易地在严格和惰性语言之间转换实现;它全部都是关于哪种语言更好地表达哪种想法。)

blog post可以更好地解释这一切。

答案 2 :(得分:2)

  

所以,我要求任何表现出懒惰的例外/例子。

点击haskell.org上的Lazy以获取规范示例。还有许多其他例子可以说明延迟评估的概念,这种概念可以从不执行程序逻辑的某些部分中获益。懒惰肯定不是很慢,但与大多数命令式编程语言共同的急切评价相反。

答案 3 :(得分:2)

懒惰是非严格的功能评估的结果。考虑一下&#34;无限&#34; 1s列表:

ones = 1:ones

在定义时,(:)函数未被评估; ones只是在必要时承诺这样做。这样的时间是你模式匹配的时候:

myHead :: [a] -> a
myHead (x:rest) = x

调用myHead ones时,需要xrest,但针对1:ones的模式匹配只会将x绑定为1和{{1}到rest;我们现在还不需要进一步评估ones,所以我们不会。

无限列表的语法,使用ones&#34;运算符&#34;对于算术序列,对于..enumFrom的调用是糖。那是

enumFromThen

所以,懒惰只是来自对-- An infintite list of ones ones = [1,1..] -- enumFromThen 1 1 -- The natural numbers nats = [1..] -- enumFrom 1 的非严格评价。

答案 4 :(得分:1)

与其他语言不同,Haskell将对象的创建和定义分离出来......您可以使用Debug.Trace轻松地观察它。

您可以像这样定义一个变量

aValue = 100

(右侧的值可能包含复杂的评估,但让我们保持简单)

要查看此代码是否被调用,您可以将表达式包装在Debug.Trace.trace中,如下所示

import Debug.Trace

aValue = trace "evaluating aValue" 100

请注意,这并没有改变aValue的定义,它只是强制程序输出&#34;评估aValue&#34;每当在运行时实际创建此表达式时。

(另请注意,trace被认为对生产代码不安全,只应用于调试。

现在,尝试两个实验....写两个不同的main s

main = putStrLn $ "The value of aValue is " ++ show aValue

main = putStrLn "'sup"

运行时,您将看到第一个程序实际创建了一个值(您将看到&#34;创建一个值&#34;消息,而第二个没有。

这是懒惰的想法....您可以根据需要在程序中放置尽可能多的定义,但只有那些使用的定义才会在运行时实际创建。

对于无限大小的物体,可以看到它的实际用途。许多列表,树等具有无限数量的元素。你的程序只会使用一些有限数量的值,但是你不想用这个混乱的事实弄乱对象的定义。以这里的其他答案中给出的无限列表为例....

[1..] -- = [1,2,3,4,....]

你可以再次使用trace看到懒惰行动,虽然你必须在扩展形式中写出[1 ..]的变体才能做到这一点。

f::Int->[Int]
f x = trace ("creating " ++ show x) (x:f (x+1)) --remember, the trace part doesn't change the expression, it is just used for debugging

现在您将看到只创建了您使用的元素。

main = putStrLn $ "the list is " ++ show (take 4 $ f 1)

的产率     创造1     创造2     创造3     创造4     清单是[1,2,3,4]

main = putStrLn "yo"

不会显示正在创建的任何项目。