Functional programming 理解Haskell中的惰性评估

Functional programming 理解Haskell中的惰性评估,functional-programming,lazy-evaluation,haskell,Functional Programming,Lazy Evaluation,Haskell,我正在努力学习Haskell,但是我被困在理解惰性评估中。 有人能详细解释我的懒惰评估和以下两个案例的输出[解释]与下面给出的有关吗 伪代码: x = keyboard input (5) y = x + 3 (=8) echo y (8) x = keyboard input (2) echo y 案例1:静态绑定、延迟计算 案例2:动态绑定,惰性评估 我需要知道最后一行(echo y)将打印什么…在上述两种情况下。对不起,这太长了,但是 恐怕答案在很大程度上取决于这些词的意思 首先,这里是

我正在努力学习Haskell,但是我被困在理解惰性评估中。 有人能详细解释我的懒惰评估和以下两个案例的输出[解释]与下面给出的有关吗

伪代码:

x = keyboard input (5)
y = x + 3 (=8)
echo y (8)
x = keyboard input (2)
echo y
案例1:静态绑定、延迟计算

案例2:动态绑定,惰性评估


我需要知道最后一行(
echo y
)将打印什么…在上述两种情况下。

对不起,这太长了,但是

恐怕答案在很大程度上取决于这些词的意思

首先,这里是Haskell中的代码(它使用静态绑定和惰性计算):

它打印
8
8
。没什么不同

现在在C++中使用严格的评估和静态绑定:

delayedAssign('x', as.numeric(readLines(n=1)))
delayedAssign('y', x + 3)
print(y)

delayedAssign('x', as.numeric(readLines(n=1)))
print(y)
#include <iostream>

int main() {
    int x;
    std::cin >> x;
    int y = x + 3;
    std::cout << y << "\n";
    std::cin >> x;
    std::cout << y << "\n";
}
评估工作就像
f
g(hx)
之前得到评估一样,即评估 进入“外部->内部”。实际上,这意味着如果

f x = 2
ie扔掉了它的论点,
g(hx)
永远不会被计算

但我认为这不是“懒惰”的问题所在 评价”。我认为这是因为:

  • +
    始终计算其参数<代码>+与使用lazy时相同 评估与否

  • 唯一可能实际延迟的计算是
    键盘输入
    -- 这不是真正的计算,因为它会导致一个动作发生; 也就是说,它从用户处读取

Haskell的人通常不会称之为“惰性评估”——他们会称之为 这是一种懒惰(或延迟)的执行

那么懒惰的执行对你的问题意味着什么呢?这意味着 操作
键盘输入
延迟。。。直到值
x
真的 需要。在我看来,这种情况发生在这里:

echo y
因为在这一点上,您必须向用户显示一个值,因此您必须知道是什么 x是!那么,延迟执行和静态绑定会发生什么呢

x = keyboard input     # nothing happens
y = x + 3              # still nothing happens!
echo y (8)             # y becomes 8. 8 gets printed.
x = keyboard input (2) # nothing happens
echo y                 # y is still 8. 8 gets printed.
现在谈谈“动态绑定”这个词。它可能意味着不同的事情:

  • 变量范围和生存期在运行时确定。这是什么语言 像R一样,不声明变量

  • 计算公式(如
    y
    的公式是
    x+3
    )不是 检查直到评估变量

  • 我猜这就是“动态绑定”在您的问题中的含义。去 通过动态绑定(sense 2)和延迟执行再次检查代码:

    我不知道有哪种语言会在最后一行打印
    7
    。。。但我
    我真的认为这就是问题希望发生的事情

    Haskell中的惰性计算:最左最外+图约简

    平方x=x*x

    广场(42号广场)

    (平方42)*(平方42)->由于图形缩减,平方42将只计算一次

    (42*42)*(正方形42)

    (1764)*(方框42)->下一步是图形缩减

    1764*1764
    =3111696

    最左边最里面的(Java、C++)

    广场(42号广场)

    正方形(42*42)

    广场(1764)

    1764*1764
    =3111696

    Haskell中惰性计算的关键在于它根本不会影响程序的输出。您可以阅读它,就好像所有内容在定义后都进行了评估,您仍然会得到相同的结果

    惰性计算只是计算程序中表达式值的一种策略。有很多种可能,它们都给出相同的结果[1];任何改变课程含义的评估策略都不是有效的策略

    因此,从某种角度来看,如果惰性评估给您带来麻烦,您不必理解它。当你在学习Haskell时,尤其是当它是你的第一种功能性的纯语言时,考虑用这种方式表达自己就更重要了。我还认为,训练自己熟悉Haskell(通常相当密集)的语法比完全“摸索”惰性评估更重要。因此,如果这个概念给你带来困难,不要太担心

    也就是说,我的解释如下。我没有使用过您的示例,因为它们实际上不受惰性计算的影响,而且Owen比我更清楚地谈到了动态绑定和延迟执行


    (有效的)评估策略之间最重要的区别在于,某些策略可能根本无法返回另一种策略可能成功的结果。惰性评估有一个特殊的属性,即如果任何(有效的)评估策略都能找到结果,惰性评估就会找到它。特别是,生成无限数据结构然后只使用有限数据量的程序可以以延迟计算终止。在您可能已经习惯的严格评估中,程序必须先完成无限数据结构的生成,然后才能继续使用其中的一部分,当然它会这样做

    惰性评估实现这一点的方法是,只在需要确定下一步要做什么时对某个东西进行评估。当您调用一个返回列表的函数时,它会立即“返回”,并为列表提供一个占位符。该占位符可以传递给其他函数、存储在其他数据结构中的任何内容。只有当程序需要了解列表的某些信息时,才会对其进行实际评估,并且只在需要时进行评估

    假设如果列表为空,程序现在将执行与列表为空时不同的操作。最初返回占位符的函数调用将进一步计算,以查看它是否返回空列表或带有head元素的列表。然后评估再次停止,因为程序现在知道该走哪条路了。如果列表中的其他部分从来都不是nee
    echo y
    
    x = keyboard input     # nothing happens
    y = x + 3              # still nothing happens!
    echo y (8)             # y becomes 8. 8 gets printed.
    x = keyboard input (2) # nothing happens
    echo y                 # y is still 8. 8 gets printed.
    
    x = keyboard input     # nothing happens
    y = x + 3              # still nothing happens!
    echo y (8)             # y becomes 8. 8 gets printed.
    x = keyboard input (2) # nothing happens
    echo y                 # y is already evaluated, 
                           # so it uses the stored value and prints 8