【问题标题】:Could `if-then-else` (always) be replaced by a function call?`if-then-else`(总是)可以被函数调用替换吗?
【发布时间】:2018-01-20 23:24:55
【问题描述】:

这个问题是出于对 PL 如何工作的好奇,而不是其他任何事情。 (它实际上是在查看 SML 时想到的,它与 Haskell 的不同之处在于前者使用按值调用 - 但我的问题是关于 Haskell。)

Haskell(据我所知)具有“按需调用”语义。 这是否意味着如果我定义一个函数如下:

cond True thenExp elseExp = thenExp 
cond _ thenExp elseExp = elseExp

这将始终与 if-then-else 表达式完全一样? 或者,在另一种意义上,是否可以将 if-then-else 视为可以定义为函数的东西的语法糖?


编辑:

为了对比 Haskell 与标准 ML 的行为,定义(在 SML 中)

cond p t f = if p then t else f;

然后是阶乘函数

fun factc n = cond (n=0) 1 (n * factc (n-1));

评估factc 1(比如说)永远不会结束,因为cond 的最后一个参数中的递归永远不会终止。

然而,定义

fun fact n = if n=0 then 1 else fact (n-1);

按照我们的预期工作,因为 then 分支仅在需要时进行评估。

也许有一些巧妙的方法可以在 SML 中推迟参数评估(不知道,因为我还不太熟悉),但关键是在按值调用类型的语言中,if-then-else经常表现不同。 我的问题是这(按需要调用与按值调用)是否是这种差异背后的主要原因(并且共识似乎是“是”)。

【问题讨论】:

  • 你的代码应该可以完美运行
  • 我也会在这里使用False,因为第一个表达式可能是undefined
  • @WillemVanOnsem 我确实想知道除了False之外的其他任何东西,虽然我不太熟悉它,但没有想到undefined
  • @WillemVanOnsem 由于模式匹配,该函数在第一个参数中已经很严格。 True。所以使用 _False 不会有什么不同。
  • 另外请记住,在 Haskell 中,与在许多具有代数数据类型的 (FP) 语言中一样,if 条件不如case of(和其他相关的模式匹配结构)重要。通常,好的 Haskell 代码使用布尔值的频率要低得多。在 Haskell 中,我们永远不会有 Java 的 Scanner.hasNext()next(),后者在第一个返回 false 时抛出。我们只需要next :: .. -> Maybe Stuff(或一些单子变体),其中布尔值消失了,有利于更好的类型。

标签: haskell lazy-evaluation


【解决方案1】:

就像Haskell Wikipedia on if-then-else 所说:

对于处理条件,`if-then-else` **语法在 Haskell98** 中定义。然而,它可以简单地用函数 `if'` 代替 和
if' :: Bool -> a -> a -> a
if' True  x _ = x
if' False _ y = y

所以如果我们使用上面的 if' 函数,并且我们需要评估它(因为 Haskell 是惰性的,我们不需要评估 if-then-@ 987654328@ 表达式),Haskell 将首先评估第一个操作数以确定它是True 还是False。如果是True,则返回第一个表达式,如果是False,则返回第二个表达式。请注意,这 not 本身意味着我们(完全)评估这些表达式。只有当我们需要结果时,我们才会评估表达式。

但如果条件是True,则根本没有理由评估第二个表达式,因为我们忽略了它。

如果我们在表达式树的多个部分共享一个表达式,那么另一个调用当然有可能(部分)评估另一个表达式。

ghci 甚至可以选择覆盖if <expr> then <expr> else <expr> 语法:-XRebindable 标志。除了其他事情之外,它还会:

条件(例如if e1 then e2 else e3)表示ifThenElse e1 e2 e3。但是大小写表达式不受影响。

【讨论】:

    【解决方案2】:

    是的,惰性语言只会在需要时才计算表达式。因此,将表达式的 then/else 部分转换为函数参数是没有问题的。

    这与 Idris 或 OCaml 等严格的语言不同,其中函数调用的参数在被调用函数执行之前进行评估。

    【讨论】:

    • 尽管您总是可以通过将参数包装在 lambda 中来简单地引入惰性(并且您可以使用宏在语言中隐藏这种包装)。例如,Smalltalk 以与 lambda 演算中布尔值的 Church 编码基本相同的方式实现条件。
    【解决方案3】:

    是的,您可以将if/then/else 视为语法糖。事实上,一门编程语言甚至不需要布尔原语,因此您也可以将TrueFalse 视为语法糖。

    lambda calculus 将布尔值定义为一组接受两个参数的函数:

    true = λt.λf.t
    false = λt.λf.f
    

    这两个函数都是接受两个值的函数,t 代表 truef 代表 false。函数true 总是返回t 值,而函数false 总是返回f 值。

    在 Haskell 中,您可以像这样定义类似的函数:

    true  = \t f -> t
    false = \t f -> f
    

    然后您可以编写 cond 函数,例如:

    cond = \b t f -> b t f
    

    例子:

    Prelude> cond true "foo" "bar"
    "foo"
    Prelude> cond false "foo" "bar"
    "bar"
    

    阅读 Travis Whitaker 的文章 Scrap Your Constructors: Church Encoding Algebraic Types 了解更多信息。

    【讨论】:

    • 我(顺便)熟悉布尔值的 lambda 演算编码,但我认为我正在谈论的问题与正常顺序与应用顺序评估有关......除非我错过了你的观点。
    • 另外,if-then-else could 是否是语法糖与 is 是否是语法糖是不同的问题(就像在 Haskell 中一样,对于case 表达式)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    相关资源
    最近更新 更多