【问题标题】:Lambda calculus beta reduction specific steps and whyLambda 演算 beta 减少的具体步骤和原因
【发布时间】:2016-12-17 06:05:44
【问题描述】:

来自我最近读的一本书:

第一:

????????.(????????.????????.????)(????)((????????.????)????) 最外层的 lambda 绑定???在这一点上是不可约的,因为它没有适用的论据。剩下的就是一次一层地进入这些术语,直到我们找到可简化的东西。

下一步:

????????.(????????.????)((????????.????)????) 我们可以应用 lambda 绑定 ????论据????。我们一直在寻找可以应用的条款。我们可以应用的下一件事是 lambda 绑定 ????到 lambda 项 ((????????.????)????)

我不明白。在第一部分,它说 ???????? 没有适用的参数,我大概可以理解,但是在下一部分,我认为 z 可以绑定到 ((????????.????)????) 因为如您所见,@987654327 @,???????? 的主体显然有一个可以绑定的z 参数。但本书只是忽略了头部????????,直接将n绑定到((????????.????)????)。我的意思是????????.???? 没有n 参数,为什么会被绑定?

有人可以向我解释一下吗?

【问题讨论】:

  • 太棒了。我很高兴不知道 Unicode 数学字母数字符号;现在我将不得不使用它们。
  • @chepner 哦,天哪,您看不到这些符号吗?直到我刚才在手机上查看了这个问题,我才意识到这一点。
  • 不,是我可以看到它们,我很惊讶在代码块中看到斜体文本,我不得不将它们整理出来。它们很难打字,但它们看起来如此正确。 :)
  • 我认为您要解决的是严格评估的 lambda 演算(您的缩减步骤)和惰性评估的 lambda 演算(本书的缩减步骤)之间的区别。
  • @chepner 但是 Unicode 在我的 iPhone 上不起作用。也许你不应该对它太着迷哈哈。

标签: haskell lambda functional-programming lambda-calculus


【解决方案1】:

使用正常的顺序评估,您可以在两次 beta 减少中得到答案

// beta reduction 1
λz.(λm.λn.m)(z)((λp.p)z) →β (λn.m) [m := z]
λz.(λm.λn.z)(z)((λp.p)z)

// beta reduction 2
λz.(λn.z)((λp.p)z)       →β z      [n := ((λp.p)z)]
λz.(λn.z)((λp.p)z)

// result
λz.z

第二次减少可能看起来很棘手,因为n 绑定到((λp.p)z) 但表达式只是z,所以n 被丢弃了。


使用应用订单评估,它需要一个额外的步骤

// beta reduction 1
λz.(λm.λn.m)(z)((λp.p)z) →β p      [p := z]
λz.(λm.λn.m)(z)((λp.z)z)

// beta reduction 2
λz.(λm.λn.m)(z)(z)       →β (λn.m) [m := z]
λz.(λm.λn.z)(z)(z)

// beta reduction 3
λz.(λn.z)(z)             →β z      [n := z]
λz.(λn.z)(z)

// result
λz.z

在这种场景下,无论我们使用普通订单评估还是应用订单评估,结果都是一样的。不同的评估策略有时会得出不同的结果。

重要的一点是,我们上面所做的缩减步骤不会发生直到 λz 被应用(取决于实现)。在您提供的示例代码中,从未应用过λz,因此在这种情况下,简化λz 的术语只是为了练习。

我们所做的只是证明 lambda 等价性(在两种不同的评估策略下)

λz.(λm.λn.m)(z)((λp.p)z) <=> λz.z

【讨论】:

  • 我明白了。如果我这样做 [n := ((λp.p)z)] 答案将是 λz.z
  • 你可以做[n := anything],答案仍然是z,因为λn.z的任期内没有n
  • 在第二次减少的时候,我以为我可以做到[z := ((λp.p)z)],最后得到z
  • 这不是它的评估方式。您有一个(λn.z)((λp.p)z) 的应用程序。 (λn.z) 只绑定n,不绑定z
  • 我指的是λz.(λn.z) 中的λz
【解决方案2】:

关键是 n 从未在抽象中使用。 n 仍然绑定到 ((λp.p)z),但它会立即被丢弃。

λz.(λm.λn.m)(z)((λp.p)z) = λz.(λn.z)((λp.p)z)  Replacing m with z
                         = λz.z                Replacing n with ((λp.p)z)

【讨论】:

    【解决方案3】:

    Lambda 表示法解析起来有点奇怪。 IMO Haskell 语法更清晰:

    \z -> (\m -> \n -> m) z ((\p -> p) z)
    

    或者,更明确

    \z -> (
            (
               ( \m -> (\n -> m) )
               z
            )
            ( (\p -> p) z )
          )
    

    第一个缩减步骤是

    \z -> (
            (
               (\n -> z)
            )
            ( (\p -> p) z )
          )
    

    \z -> (
            (\n -> z)
            ( (\p -> p) z )
          )
    

    那么你确实可以绑定((\p -&gt; p) z)——不是z,而是n! (但实际上根本没有使用。)

    \z -> (
            (z)
          )
    

    或者只是\z -&gt; z。所以,我们仍然拥有z lambda,正如书中所说,这是不可逆转的。我们只是没有别的东西!


    ...我不确定这是否真的是您的问题。如果是这样:为什么我们不首先看看我们是否可以减少((\p -&gt; p) z),那么答案是,我认为,lambda 演算根本没有定义这个(它只是定义了什么您可以应用的转换,而不是您应该按什么顺序进行。其实我不确定,如果我错了,请纠正我)。在像 Scheme 这样严格的语言中,您确实会首先减少 ((\p -&gt; p) z); Haskell 不会这样做,因为没有必要。不管怎样,这并不重要,因为无论如何结果都会被丢弃。

       ( \z -> (\n -> z) ((\p -> p) z) )
    ≡  ( \z -> (\n -> z) z )
    ≡  ( \z -> (\n -> z) foo )
    ≡  ( \z -> z )
    

    【讨论】:

    • 嗨!谢谢回复,今天脑洞大开。凌晨 2 点 26 分,该睡觉了。我想明天我的大脑确实存在时,我将不得不再次阅读您的答案。大声笑。
    • 好吧,我的想法是,当我到达 ( \z -> (\n -> z) ((\p -> p) z) ) 时,我可以这样做:bind z到 (\p -> p) z) 并得到 (\n -> ((\p -> p) z)) 并将 n 绑定到 z 并得到 ((\p -> p) z) 然后 (\p -> p)?
    • 对不起 ((\p -> p) z) 应该是 z,我的错。所以这意味着“将 z 绑定到 (\p -> p) z)”是错误的。为什么?
    • 不,你不能将z 绑定到任何东西,因为它仍然来自范围之外。因此,我对 lambda 语法的易读性发表了评论。 λa . b c d 只是λa . ((b c) d) 的简写,即a-lambda 总是在最外面;在它之后没有任何东西可以绑定它。
    • 我个人觉得这比“奇怪”的 lambda 表示法更难阅读,哈哈。尽管如此,在一个解释清楚的答案上做得很好^_^
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多