我对Haskell很新,可能是一个愚蠢的问题。 我想要的是将我的函数作为参数赋予任何运算符。 例如:
myFunc :: a -> Int -> Int -> Boolean
myFunc operator a b = a operator b
*Project> myFunc (>) 5 2
True
*Project> myFunc (<=) 5 2
False
请告诉我如何做到这一点!
答案 0 :(得分:8)
您可以使用haskell函数参数执行此操作。
在上面的函数中,您希望myFunc
使用一个需要两个Int
并返回Bool
的函数(不一个Boolean
,你必须打错了)。该函数的声明为(Int -> Int -> Bool)
。因此,您可以写:
myFunc :: (Int -> Int -> Bool) -> Int -> Int -> Bool
myFunc op a b = a `op` b
这定义了一个高阶函数,它接受一个带有两个Int
参数的函数,返回Bool
(和两个Int
s)。您现在可以像使用任何其他函数参数一样使用它!
请注意,这与执行完全相同:
myFunc (#) a b = a # b
或者:
myFunc (%) a b = a % b
因为使用像*
或/
这样的中缀操作符,或者只包含特殊字符的任何操作符,没有反引号只是使用它们的简写(键入{{1每次你想分开一些东西都会很烦人!)。
答案 1 :(得分:6)
在幕后,功能&#34;存在&#34;没有名字。您定义的任何函数(或已在库中定义的函数),例如myFunc
只是 是一个函数值,并且该名称只是为我们提供了一种在其他代码中引用它的方法想要用它。这与您编写x = 3
时完全相同:值3
&#34;存在&#34;与名称x
无关,该名称只是为我们提供了一种引用它的方法。
为什么这与您关于传递运算符的问题相关?
嗯,就Haskell而言,像>
和<=
这样的运算符也只是无名函数恰好绑定到名称>
和<=
。它们作为运算符的特殊处理(你可以在你调用它们的参数之间写入它们)只是关于 names ,如果你用不同的名称引用它们就会改变。
Haskell中有两种类型的名称。字母数字名称(仅由字母,数字和下划线组成)和符号名称(仅由符号字符组成)。如果您有一个表达式{1} {2} {3}
,那么{2}
是一个符号名称({1}
和{3}
是{2}
和{1}
&#34;上调用{3}
。但如果它们都不是符号名称,那么它会被解释为&#34;在{1}
和{2}
&#34;。上调用{3}
。 1
但所有这一切只有在参考名称时才会发生,而不是这些名称实际引用的功能。所以,如果你这样写myFunc
:
myFunc operator a b = operator a b
然后,myFunc
被称为myFunc (+) 1 2
还是像myFunc plus 1 2
一样,实际上并不重要;在myFunc
&#34;运算符&#34;的定义内名称operator
引用,这是一个字母数字名称。所以当你想要调用它时,你把它放在第一位,其参数跟随。
或者您可以在myFunc
中使用符号名称,如下所示:
myFunc ($&^*) a b = a $&^* b
同样,即使使用myFunc
之类的非运算符函数调用myFunc plus 1 2
,这也会有效。
当然,有一些方法可以将任何一种名称转换为另一种名称;您可以在反引号中输入一个字母数字名称,以便像操作符一样使用它作为中缀:
myFunc operator a b = a `operator` b
你可以在括号中加上一个符号名称来简单地用它作为它所绑定函数的引用(这实际上是使用运算符而不为其提供参数的唯一方法):
myFunc ($^&*) a b = ($&^*) a b
基本上,将操作符传递给函数需要知道的唯一特殊事项是您已经知道的:在调用函数时将操作符放在括号中。在函数的定义中,您可以将其写成与任何其他函数完全相同;您在该功能定义中选择的名称样式将决定您是将其称为操作符还是普通函数。你不需要知道(实际上不能发现)是否是运营商&#34;在外面&#34;功能。
1 当然,当你有更复杂的表达式涉及3个以上的东西和多个运算符时,优先级和关联性的规则就会发挥作用,以确定究竟发生了什么。