【问题标题】:Haskell application, or section?Haskell 应用程序或部分?
【发布时间】:2014-12-27 18:06:06
【问题描述】:

我想翻转列表构造函数的用法,使其具有类型:

[a] -> a -> [a]

(用于折叠),所以尝试了:

(flip :)

但它给出了类型:

Prelude> :t (flip :)
(flip :) :: [(a -> b -> c) -> b -> a -> c] -> [(a -> b -> c) -> b -> a -> c]

这让我感到惊讶,但似乎这被解析为 (:) 的左侧部分,而不是翻转的部分应用。使用翻转作为中缀重写它似乎可以克服这个问题,

Prelude> :t ((:) `flip`)
((:) `flip`) :: [a] -> a -> [a]

但是我找不到定义这种行为的规则,我认为函数应用是最高优先级,并且被左->右评估,所以我希望这两种形式是等价的。

【问题讨论】:

    标签: haskell operators


    【解决方案1】:

    你想做的是这样的:

    λ> :t (flip (:))
    (flip (:)) :: [a] -> a -> [a]
    

    Haskell 中的运算符是中缀的。因此,当您执行flip : 时,它以中缀方式运行,即flip 应用于: 函数。通过在flip (:) 中显式放置括号,您可以告诉: 应该应用于flip。您还可以使用flip 中的反引号运算符来制作您已经尝试过的中缀。

    【讨论】:

    • 打破它可以帮助您看到一致性:let cons = (:) in flip cons 有效,let cons = : in flip cons 是一个错误
    【解决方案2】:

    : 放在括号中使您的第二个示例有效,而不是在flip 周围使用反引号。

    我们经常说“函数应用具有最高优先级”来强调,例如f x + 1 应该读作(f x) + 1,而不是f (x + 1)。但这并不完全准确。如果是这样,并且(flip :) 已按您的预期解析,那么在(f x) + 1 之后的最高优先级将是(f x)+ 的应用;整个表达式 f x + 1 最终会被解析为 f 应用于 3 个参数:x+1。但这会发生在所有涉及中缀运算符的表达式中!即使是简单的1 + 1 也会被识别为1 应用于+1(然后抱怨缺少Num 实例,这将使1 成为一个函数)。

    本质上,对“函数应用程序具有最高优先级”的这种严格理解意味着函数应用程序将是所有发生的事情;中缀运算符总是作为某个函数的参数结束,而实际上从不作为中缀运算符。

    实际上,优先级(和关联性)是解决涉及多个中缀运算符的表达式的歧义的机制。函数应用程序不是中缀运算符,并且根本不参与优先/关联系统。不涉及运算符的术语链被解析为函数应用程序 before 优先级被调用来解析运算符应用程序(因此“最高优先级”),但它不是 真正 优先级导致它。

    这是它的工作原理。您从术语和运算符的线性序列开始;没有结构,它们只是挨着写。

    我在这里所说的“术语”可以是非运营商标识符,例如flip;或字符串、字符或数字文字;或列表表达式;或带括号的子表达式;等等。就这个过程而言,它们都是不透明的;我们只知道(并且只需要知道)它们不是中缀运算符。我们总是可以告诉运算符,因为它要么是像++!@> 这样的“符号”标识符,要么是反引号中的字母数字标识符。

    因此,术语和运算符的序列。您会在一行中找到一个或多个术语的所有链,其中不包含任何运算符。每个这样的链都是一个功能应用链,并成为一个单独的术语。1

    现在,如果您有两个直接相邻的运算符,则会出现错误。如果您的序列以运算符开始或结束,这也是一个错误(除非这是一个运算符部分)。

    此时,您可以保证有一个严格交替的序列,例如 term operator term operator term operator term 等。因此,您选择具有最高优先级的运算符及其左右两侧的术语,称其为运算符应用程序,这三个项目成为一个术语。当您有多个具有相同优先级的运算符时,关联性充当平局。冲洗并重复,直到整个表达式变成一个术语(或关联性无法打破平局,这也是一个错误)。这意味着在涉及运算符的表达式中,“顶级应用程序”始终是运算符之一,而不是普通的函数应用程序。

    这样做的结果是,在没有任何情况,运算符最终可以作为参数传递给函数。这简直是​​不可能的。这就是为什么我们需要(:) 语法来禁用运算符的“运算符性”,并将它们的身份作为值来获取。

    对于flip :,唯一的非运算符术语链就是flip,因此没有普通的函数应用程序可以“以最高优先级”解析。 : 然后去寻找它的左和右参数(但这是一个部分,所以没有右参数),并在它的左边找到flip

    要使flip 接收: 作为参数而不是相反,您必须编写flip (:)(:) 不是运算符(它在括号中,所以里面的内容无关紧要),所以我们有一个没有运算符的两个术语链,因此通过将 flip 应用于 @ 可以解析为单个表达式987654353@.


    1 另一种看待这个问题的方法是,您识别所有未被运算符分隔的术语序列,并在它们之间插入“函数应用运算符”。此“运算符”的优先级高于分配给其他运算符的优先级,并且是左关联的。然后,运算符解析逻辑将自动按照我描述的方式处理函数应用程序。

    【讨论】:

    • 谢谢 - 我知道括号,只是忘了做!运算符和函数应用的优先级说明很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 2011-09-29
    相关资源
    最近更新 更多