两次函数调用,但仅显示一条轨迹

杰费拉德

使用GHC版本8.0.2的以下程序:

import Debug.Trace

f=trace("f was called")$(+1)

main = do
    print $ f 1
    print $ f 2

输出:

f was called
2
3

这是预期的行为吗?如果是,为什么?我希望该字符串f was called可以打印两次,一次打印之前2,一次打印之前3

在TIO上的结果相同:在线试用!

编辑但是这个程序:

import Debug.Trace

f n=trace("f was called:"++show n)$n+1

main = do
    print $ f 1
    print $ f 2

输出:

f was called:1
2
f was called:2
3

在线尝试!

我怀疑这些行为与懒惰有关,但我的问题仍然存在:这是预期的行为吗?如果是,为什么?

黑客断言:

跟踪函数将输出作为其第一个参数给出的跟踪消息,然后返回第二个参数作为结果。

我在第一个示例中没有看到它。

编辑2基于@amalloy注释的第三个示例:

import Debug.Trace

f n=trace "f was called"$n+1

main = do
    print $ f 1
    print $ f 2

输出:

f was called
2
f was called
3
Ben

确实是懒惰的结果。

懒惰意味着仅定义一个值并不意味着将对它进行评估。只有在需要某些东西时才会发生。如果不需要它,则实际产生它的代码不会“做任何事情”。如果一个特定的值所需的代码运行,但只有第一次将需要; 如果还有其他引用相同的值并再次使用,则这些使用将直接使用第一次生成的值。

您必须记住,函数在每种意义上都是价值适用于普通值的所有内容也适用于函数。因此,您的定义f仅是为一个值编写一个表达式,该表达式的求值将推迟到f实际需要该值时为止,并且因为它需要两倍于值(函数)的值,该表达式计算的值将被保存并第二次重用。

让我们更详细地看一下:

f=trace("f was called")$(+1)

您正在f用一个简单的方程式定义一个值(不使用任何语法糖来在方程式的左侧写自变量,或通过多个方程式提供案例)。因此,我们可以简单地将右侧作为定义值的单个表达式f仅仅定义它什么都不做,它坐在那里直到您调用:

print $ f 1

现在print需要评估其参数,因此这将强制表达式f 1但是我们f必须1先强制才能申请f因此,我们需要弄清楚表达式trace "f was called" $ (+1)求值的功能所以trace实际上是所谓的,做它的不安全IO印刷f was called出现在终端,然后trace返回第二个参数:(+1)

所以,现在我们知道什么功能f是:(+1)f现在将直接引用该函数,trace("f was called")$(+1)如果f再次调用该函数,则无需评估原始代码这就是为什么第二个print什么都不做的原因

即使看起来很相似,这种情况也大不相同:

f n=trace("f was called:"++show n)$n+1

在这里,我们正在使用的语法糖通过在左侧写参数定义的功能。让我们对lambda表示法进行解糖,以更清楚地了解绑定到的实际值f是:

f = \n -> trace ("f was called:" ++ show n) $ n + 1

在这里,我们直接编写了一个函数值,而不是可以通过计算得出一个函数的表达式。因此,在f需要对其进行调用之前需要对其进行评估时1,的值f就是整个函数;trace呼叫是内部的功能是被称为到的东西,而不是产生一个功能。因此trace,不被称为评估的一部分f,而是被称为评估应用程序的一部分f 1如果您保存了该结果(例如执行let x = f 1),然后多次打印,则只会看到一条迹线。但是当我们进行求值时f 2trace调用仍然存在于函数内部,即f因此,当f再次被调用也是如此trace

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章