Shadowing and Nested function

9

我希望了解阴影和嵌套函数的机制是如何工作的。

例如:

let func y =
    let dup y = y + y
    let z = dup y
    let dup y =
        let dup z =
            let y = y * z
            y
        let z = y
        y
    dup z + z;;

val func : int -> int

> func 3;;
val it : int = 12

有人能解释一下这里发生了什么吗?

哇,这是什么疯狂的机器啊。 - Dykam
2个回答

17

你的代码等效于以下代码,其中我只是对你的变量进行编号以帮助你可视化阴影效应的发生。

let func y0 = 
  let dup0 y1 = y1 + y1
  let z0 = dup0 y0
  let dup1 y2 = 
    let dup2 z1 = 
      let y3 = y2 * z1 
      y3
    let z2 = y2 
    y2 
  dup1 z0 + z0

当然,这可以进一步简化。由于dup2z2从未使用过,dup1等价于let dup1 y2 = y2,整个函数等价于

let func y0 =
  let dup0 y1 = y1 + y1
  let z0 = dup0 y0
  dup1 z0 + z0

等同于

let func y0 =
  let z0 = y0 + y0
  z0 + z0

通过代换。这与

let func y0 = 4 * y0

这有帮助吗?


不仅如此,现在我明白了“rec”关键字的必要性。 - Neo

14

我认为@kvb提供了一个非常好的解释,展示了代码的求值方式。该代码结合了嵌套函数变量遮蔽,使人感到相当困惑 :-). 我认为单独看这两个概念是有用的。

变量遮蔽允许您通过在let声明或match结构中进行新值绑定来隐藏一个值。它意味着您将无法再访问原始值。以下是一个更简单的示例:

let foo num =
  let num = num + 20 // Line 2
  let num = num * 2  // Line 3
  num

这里我们声明了一个函数,它接受一个名为num的参数。假设我们将函数用1作为参数调用。在第二行,我们声明了一个新值,使用相同的名称-初始化为1 + 20,即21。第三行再次声明一个新值并将其初始化为21 * 2(因为它看到了上一个声明的num符号,它的值为21)。在第4行,我们再次访问上一个声明的num符号,并返回42

这特别有用,当你有一些计算可以计算出一些新值,而这个新值应该被所有后续的计算所使用时。阴影允许你隐藏之前的值,这样就不会意外地使用原始值。

嵌套函数在需要进行一些使用外部函数参数的局部实用计算时非常有用。例如:

let times x nums = 
  let timesUtil y = y * x
  for n in nums do
    printfn "%d" (timesUtil n)

这里我们声明了一个实用的嵌套函数timesUtil,它将任何数字乘以x的值(即times函数的参数)。然后我们可以在后面使用它(最后一行)执行操作而不必再次传递x值作为参数。因此,关于嵌套函数最有趣的事情是它们可以访问外部函数声明的变量。


嗨Tomas,在第4行,我认为应该是42而不是41 - elmattic
是的,谢谢!得到42作为结果是我的意图 - 我不确定41是怎么出现的! - Tomas Petricek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接