前缀运算符很好的一个重要原因是它们可以避免使用括号,因此+ - 10 1 2
明确表示(10 - 1) + 2
。如果丢弃了parens,那么中缀表达式会变得模棱两可,你可以通过具有某些优先级规则来消除这种情况,但那是凌乱的,等等,等等。
我想让Haskell使用前缀操作,但我能看到的唯一方法就是通过删除括号来获得收益。
Prelude> (-) 10 1
使用两个parens。
当你尝试编写函数时会变得更糟,因为
Prelude> (+) (-) 10 1 2
产生错误,我相信因为它试图将减号操作输入加号操作而不是先评估减号然后再进食 - 所以现在你需要更多的parens!
有没有办法让Haskell智能地评估前缀表示法?我想如果我做了像
这样的功能Prelude> let p x y = x+y
Prelude> let m x y = x-y
我会在较少的parens上恢复初始收益,但功能构成仍然是个问题。如果有一种聪明的方式来加入$
符号,使其表现得至少接近我想要的,我就没有看到它。如果有完全不同的策略,我很高兴听到它。
我尝试复制接受的答案:
Haskell: get rid of parentheses in liftM2
但是在Prelude控制台和Haskell脚本中,import命令都不起作用。此外,这是比我能够理解的更先进的Haskell,所以我希望在我做重任之前可能还有其他更简单的解决方案来调查这是在做什么。
答案 0 :(得分:5)
当你尝试编写函数时会变得更糟,因为
Prelude> (+) (-) 10 1 2
产生错误,我相信是因为它 试图将减号操作反馈到加号操作中 而不是先评估减去然后喂食 - 所以现在你需要甚至 更多的parens!
在这里,您提出了一个关键问题,即在Haskell中获取所需内容的阻止程序。
您所讨论的前缀表示法对于基本算术运算(更一般地说,对于静态已知的arity的任何函数集)都是明确的。但是你必须知道+
和-
每个接受+ - 10 1 2
的2个参数被明确地解析为+(-(10, 1), 2)
(其中我有明确的参数列表来表示每个调用)
但忽略+
和-
的具体含义,第二个函数作为参数的第一个函数是一个非常合理的解释!对于 Haskell 而不是算术,我们需要支持更高阶函数,如map
。您希望not not x
必须变为not(not(x))
,但map not x
必须变为map(not, x)
。
如果我有f g x
怎么办?这应该怎么样?我是否需要知道f
和g
绑定的内容,以便我知道它是not not x
还是像map not x
这样的情况,只是为了知道如何解析通话结构?即使假设我有所有可用的代码可供检查,如果我不知道任何表达式的调用结构是什么,我该如何弄清楚什么是必然的?
你最终需要发明像map (not) x
这样的消歧语法,在括号中包装not
以禁用它像arity-1函数一样的行为(就像Haskell的实际语法允许你包装运算符一样)在括号中禁用其作为中缀运算符的能力)。或者使用所有Haskell函数都是arity-1的事实,但是你必须编写(map not) x
,你的算术示例必须看起来像(+ ((- 10) 1)) 2
。回到括号!
事实是,您提出的前缀表示法并非明确无误。 Haskell的常规函数语法(没有运算符)是;规则是你总是将像foo bar baz qux etc
这样的术语序列解释为((((foo) bar) baz) qux) etc
(其中foo,bar等中的每一个都可以是括号中的标识符或子术语) 。您使用括号不消除歧义该规则,而是将术语分组以强加不同的调用结构,而不是硬规则为您提供的。
中缀运算符做使该规则复杂化,并且他们 模糊不知道所涉及的运算符(它们的优先级和关联性,与arity不同,与名称不是指的实际值)。添加了这些复杂性以帮助使代码更容易理解;特别是对于大多数程序员已经熟悉的算术约定(+
的优先级低于*
等)。
如果您不喜欢必须记住运算符的优先级和关联性(不是不合理的位置)的额外负担,那么 可以使用明确的符号而不需要优先级规则,但它必须是 Haskell的前缀表示法,而不是波兰语前缀表示法。无论你使用什么语法惯例,在任何语言中,你总是必须使用括号之类的东西来表示你需要的调用结构与标准约定所指示的不同的分组。所以:
(+) ((-) 10 1) 2
或者:
plus (minus 10 1) 2
如果您定义非运算符函数名称。