【问题标题】:What does let 5 = 10 do? Is it not an assignment operation?让 5 = 10 做什么?不是赋值操作吗?
【发布时间】:2016-01-05 12:39:18
【问题描述】:

如果我说let 5 = 10,为什么5 + 1 返回6 而不是11

【问题讨论】:

  • 立即回答将是“因为您无法重新定义 5 是什么”。然而,我对写let 5 = 10 竟然有可能感到非常惊讶!
  • 你能做的是overload+:let 1+1=3 in 1+1;)
  • 顺便说一句:我认为let 5 = 10 中的5 仍然是一种模式(只是一个永远不会匹配的模式),因此它不会绑定任何东西(如let (x,5) = (6,6)
  • @Carsten 确实——这就是Chapter 4 of the Report 所说的,假设我解析正确。
  • Prelude> :set -XBangPatterns 然后Prelude> let !5 = 10 产量*** Exception: <interactive>:9:5-11: Non-exhaustive patterns in pattern binding。懒惰隐藏了失败的模式匹配,让你的误解持续存在。

标签: haskell pattern-matching variable-assignment assignment-operator let


【解决方案1】:

当你说

let 5 = 10

这不是对5的重新定义,是模式匹配,和你说的一样

foo 5 = undefined
 ... foo 10 ...

如果曾经匹配,该模式就会失败。

在 let 表达式中,匹配是惰性的。这意味着只有在评估受其绑定的变量时才进行匹配。这使我们可以编写诸如

之类的东西
 let foo = undefined in 10

在您的表达式中,没有绑定任何变量,因此模式永远不会匹配。

可以说,这种没有变量的模式在 let-bindings 中没有意义,应该被编译器检测到,但语言并没有禁止它们。

【讨论】:

  • 令人放心的let (5,x) = (10,True) in x 提供*** Exception: <interactive>:2:5-21: Irrefutable pattern failed for pattern (5, x)
  • 这对于 let-bindings 来说是愚蠢的,但不是对所有的:例如only5 xs = [5 | 5 <- xs] 是有道理的(如果你对列表没问题fail)。
  • @user3237465 是的,我只是说 let-bindings。更新中。
  • pigworker: 和 let (~5,x) = (10,True) in x 返回 True 只是因为我们明确告诉编译器假设 5 模式匹配。
  • 类似情况:let [] = [1,2] in True
【解决方案2】:

基本上,

let 5 = 10 in ...

等价于

case 10 of ~5 -> ...

注意~,它标志着懒惰,或无可辩驳的模式。这是一个匹配所有内容的模式,它将匹配推迟到实际需要某个变量的点。模式5 中没有变量,所以什么都不会发生。

这种极端情况没什么用,可以说编译器应该在这里发出警告。

为了阐明惰性模式的含义,请考虑以下几点:

case f 3 of
  (x,y) -> g 10 x y

这里f 3首先被评估(到WHNF),暴露了对构造函数。然后x,y 绑定到(尚未评估的)对组件。最后,计算g 10,将结果应用于x(现在可能需要),然后应用于y(可能导致xy需要)。

相比之下,

case f 3 of
  ~(x,y) -> g 10 x y

不以评估 f 3 开始。相反,x 绑定到未评估的fst (f 3),而y 绑定到未评估的snd (f 3)。相反,我们从评估g 10 开始。然后,我们将其应用于x:这可能会导致需要x,从而触发f 3 的评估。然后,我们将结果应用于y,导致类似的评估。大多数实现实际上会在xy 之间共享f 3 的结果,以便最多计算一次。

【讨论】:

  • 类似的模式匹配很有用。尤其是写catch m $ \ ~MyException -> ... 并不罕见,其中非绑定惰性模式匹配是提供异常类型的便捷方式。但是以这种方式使用数字文字似乎有些牵强。
【解决方案3】:

作为@n.m。就是说,你是模式匹配。以下是一些示例。

模式匹配可以成功

Prelude> let (a, 10) = (15, 10) in a
15

或者失败。

Prelude> let (a, 10) = (15, 15) in a
*** Exception: <interactive>:5:5-22: Irrefutable pattern failed for pattern (a, 10)

由于 Haskell 是惰性的,如果您不使用结果值,您的代码将成功。这基本上就是你正在做的事情:

Prelude> let (a, 10) = (15, 15) in "Something else"
"Something else"

请注意,类型仍然必须检查:

Prelude> let (a, 10, 999) = (15, 15) in "Something else"

<interactive>:7:20: error:
    • Couldn't match expected type ‘(t, Integer, Integer)’
                  with actual type ‘(Integer, Integer)’
    • In the expression: (15, 15)
      In a pattern binding: (a, 10, 999) = (15, 15)
      In the expression: let (a, 10, 999) = (15, 15) in "Something else"
    • Relevant bindings include a :: t (bound at <interactive>:7:6)

【讨论】:

  • “由于 Haskell 是惰性的,如果你不使用结果值,你的代码将会成功”——值得一提的是 let 绑定默认匹配惰性也很重要。 case (15, 15) of { (a, 10) -&gt; "Something else" } 崩溃,而 case (15, 15) of { ~(a, 10) -&gt; "Something else" }(由于惰性匹配,is equivalent to your third example)成功。
  • @duplode,我认为 letwhere 中的惰性默认设置是个坏主意,这也是 bang 模式有时会让人感到困惑的原因。
  • @dfeuer 绑定到无限列表怎么样?例如let nats = [1..] in takeWhile isOkay nats
  • @Teodor,我指的是letwhere 中LHS 上的模式,而不是简单的变量绑定。我认为let p = e1 in e2应该和case e1 of p -&gt; e2完全一样,不管p长什么样子。不幸的是,在 Haskell 中没有解决这个问题,但希望未来的语言会考虑按照我的方式来做。
  • @dfeuer 感谢您的澄清。我同意你的观点。
猜你喜欢
  • 2015-02-02
  • 1970-01-01
  • 2011-10-18
  • 2017-09-03
  • 2011-02-13
  • 1970-01-01
  • 2013-05-24
  • 2011-05-10
  • 1970-01-01
相关资源
最近更新 更多