2016-03-07 68 views
-2

我正在学习haskell几天,懒惰就像流行语一样。因为我不熟悉懒惰(我一直主要使用非功能性语言),所以对我来说这并不容易。懒惰的本质。 Haskell

所以,我要求任何excerise /例子,告诉我什么是懒惰的事实。

在此先感谢;)

+0

取一个从0到* infinity *的数字列表,将一个函数映射到它上面(迭代它,比方说所有的值乘以2),然后打印出前10个数字。 - 懒惰,你可以完全按照描述来完成这项任务;没有它,你必须以不同的方式思考和编码。 – deceze

+3

从您的教程中引用您遇到问题的内容:定义,声明,代码片段 - 无论提及的是懒惰。解释你具体的困难是什么。 –

回答

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做了一个更好的解释所有这一切。

4

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

[1,2..] 

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

懒惰可让您根据需要获取数字。

2

所以,我要求任何excerise /例子,告诉我什么是懒惰的事实。

点击懒惰haskell.org得到规范的例子。还有很多其他的例子可以说明延迟评估的概念,它不受程序逻辑某些部分的影响。懒惰肯定不慢,但与大多数命令式编程语言通用的热切评估正好相反。

2

懒惰是非严格功能评估的结果。考虑1秒的“无限”的文章:

ones = 1:ones 

在定义的时候,(:)功能不评估;如果有必要,ones只是一个承诺。这样的时间会当你的模式匹配:

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

myHead ones被调用,xrest需要,而是针对1:ones模式匹配简单地结合x 1和restones;我们目前不需要评估ones,所以我们不需要。

使用..“运算符”作为算术序列的无限列表的语法是调用enumFromenumFromThen时的糖。也就是说

-- An infintite list of ones 
ones = [1,1..] -- enumFromThen 1 1 

-- The natural numbers 
nats = [1..] -- enumFrom 1 

如此反复,懒惰只是来源于enumFrom非严格的评估。

+0

“懒惰是非严格功能评估的结果”并不完全正确。在你的例子中,'ones'不是一个函数,它是一个(无限)列表。而在一种渴望的语言(例如OCaml)中,它确实会导致无限循环。但是我们可以利用这样的事实,即在lambda表达式下不执行评估以使用例如相似的惰性列表对模型进行建模。 'type'a stream ='a *(unit - >'a)'和defininig'let rec ones()= Cons(1,ones)'的缺点。 – gallais

+0

由于'(:)'不严格,它实际上是一个有限的列表,其中有1个,而对'ones'的调用没有被评估。当你需要用尾巴或尾巴等东西来完全评估尾巴时,清单的“无限”性质才会变得明显。 – chepner

1

与其他语言不同,Haskell分离了对象的创建和定义....您可以使用Debug.Trace轻松地观看此操作。

你可以这样定义

aValue = 100 

一个变量(在右边的值可能包括一个复杂的评估,但让我们保持简单)

要看看这个代码曾经被称为,你可以用表达的Debug.Trace.trace这样

import Debug.Trace 

aValue = trace "evaluating aValue" 100 

请注意,这不会改变的aValue定义,它只是强制程序在运行时实际创建该表达式时输出“评估值”。

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

现在,尝试了两个实验....写两个不同main小号

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

main = putStrLn "'sup" 

运行时,你将看到的第一个程序实际上创建了安勤(你会看到“创建一个值”的消息,而第二个没有。

这是懒惰的想法....你可以在程序中放置尽可能多的定义,只要你想那些使用的将在运行时实际创建。

实际使用这个可以看到无限大小的物体。许多列表,树等都有无数的元素。你的程序只会使用有限数量的值,但你不想用这个混乱的事实来混淆对象的定义。就拿在这里其他的答案中给出的无限列表....

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

你可以在这里使用跟踪再次看到懒惰的行为,虽然你将不得不写出来的一个变种[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" 

不会显示任何项被创建。