给定一些函数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))
。
答案 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
。部分适用于第二个参数可能听起来不像是交换前两个参数,但是由于懒惰的评估和讨论,它们实际上是同一个东西!