haskell中具有多个参数的部分应用程序

时间:2014-09-20 17:34:05

标签: haskell currying

给定一些函数f(x1,x2,x3,..,xN),将它部分地应用于几个地方通常很有用。例如,对于N = 3,我们可以定义g(x)= f(1,x,3)。但是,Haskell中的标准部分应用程序不能以这种方式工作,只允许我们通过修复其第一个参数来部分应用函数(因为所有函数实际上只接受一个参数)。有没有简单的方法来做这样的事情:

g = f _ 2 _
g 1 3

输出f 1 2 3的值? 当然我们可以做一个lambda函数

g=(\x1 x3 -> f x1 2 x3)

但我觉得这很难读。例如,在Mathematica中,它就像这样,我发现它非常好:

g=f[#1,2,#2]&
g[1,3]

输出f[1,2,3]

编辑:也许我应该更多地谈论动机。我想在点样式合成中使用这种部分应用的函数,即在这样的表达式中使用:

h = g. f _ 2 . k

获取h 3 = g(f(k(3),2))

5 个答案:

答案 0 :(得分:2)

你可以阅读this question关于如何更改参数的顺序,然后使用部分应用程序,但实际上目前在Haskell中最干净,最清晰的方法就是直接:

g x y = f x 2 y

答案 1 :(得分:1)

不,最简单的方法是定义一个lambda。您可以尝试使用flip,但我怀疑它会比lambda更干净,更简单。特别是对于更长的参数列表。

答案 2 :(得分:1)

最简单(和规范)的方法是定义一个lambda。如果您在可能的情况下使用有意义的参数名称,则可读性更高

getCurrencyData :: Date -> Date -> Currency -> IO CurrencyData
getCurrencyData fromDate toDate ccy = {- implementation goes here -}

您可以使用lambda语法

定义新函数
getGBPData = \from to -> getCurrencyData from to GBP

或没有它

getGBPData from to = getCurrencyData from to GBP

或者你可以使用组合器,但我认为这很难看

getGBPData = \from to -> getCurrencyData from to GBP
           = \from to -> flip (getCurrencyData from) GBP to
           = \from    -> flip (getCurrencyData from) GBP
           = \from    -> (flip . getCurrencyData) from GBP
           = \from    -> flip (flip . getCurrencyData) GBP from
           =             flip (flip . getCurrencyData) GBP

答案 3 :(得分:1)

没有一般方法可以执行您要求的内容,但有时您可以使用中缀部分作为flip的替代。使用你的上一个例子:

g . (`f` 2) . k

此外,我想指出,如果您重新排序函数所使用的参数,有时它会有所帮助。例如,如果你有一个特别经常部分应用于一个参数的函数,你应该把它作为第一个参数。

假设你正在实现一个代表2D游戏板的数据结构(就像你可能用于国际象棋程序),你可能希望你的getPiece函数的第一个参数是棋盘,第二个参数是位置。我认为被检查的位置可能会比董事会更频繁地更改。当然,这并不能解决一般问题(也许你想检查一下电路板列表中的相同位置),但它可以减轻它。当我决定参数顺序时,这是我考虑的主要内容。

答案 4 :(得分:1)

需要考虑的其他事项:

为部分应用程序模式定义辅助函数(本地或全局,如果您发现自己多次使用少量模式)。

fix_2 f a = \x -> f x a
fix1_3 f a b = \x -> f a x b

h = g . fix_2 f 2 . k

不如你假设的“空白”语法那么好,但没关系;您可以将fix_2读作标识正在使用的部分应用程序 1 的标记。

请注意,您永远不需要任何不修复最后一个参数的部分应用程序方案;用currying修复4参数函数的第一个和第三个参数(留下两个参数函数)与修复3参数函数的第一个和第三个参数相同。

作为一个更复杂的想法,我相信你应该能够编写一个模板Haskell quasiquoter,它实际上实现了你的“下划线是一个隐含函数的参数”pseudosyntax。这将允许你写下表达式:

h = g . [partial| f _ |] . k

比辅助函数更多的语法开销,你仍然需要一个额外的名称(以识别quasiquoter),但如果你的部分应用程序更加复杂,可能更容易阅读:

h = g . [partial| f 1 _ 3 4 _ 6 |] . k

这意味着实现模板Haskell代码的一大堆工作,我从来没有做过,所以不知道多少。但是你有任意的部分应用程序,而辅助函数路径需要为每个模式手动定义。


1 请注意,仅修复第二个参数已经具有标准辅助函数:flip。部分适用于第二个参数可能听起来不像是交换前两个参数,但是由于懒惰的评估和讨论,它们实际上是同一个东西!