6

给定一些函数 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)).

4

5 回答 5

3

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

g x y = f x 2 y
于 2014-09-20T18:07:51.487 回答
2

其他需要考虑的事项:

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

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,它实际上实现了您的“下划线是隐含函数的参数”伪语法。这将允许您编写表达式谎言:

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

比辅助函数更多的语法开销,并且您仍然需要涉及一个额外的名称(以标识准引用器),但如果您的部分应用程序方案更复杂,可能更容易阅读:

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

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


1请注意,仅修复第二个参数已经具有标准辅助函数:flip. 部分应用于第二个参数可能听起来不像交换前两个参数,但由于惰性评估和柯里化,它们实际上是一回事!

于 2014-09-21T00:40:01.590 回答
2

没有通用的方法来做你所要求的,但你有时可以使用中缀部分作为flip. 使用你的最后一个例子:

g . (`f` 2) . k

另外,我想指出,如果您重新排序函数所采用的参数,有时会有所帮助。例如,如果您有一个通常会部分应用于一个参数的函数,那么您可能应该将其作为第一个参数。

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

于 2014-09-20T20:44:49.790 回答
1

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

于 2014-09-20T17:40:47.197 回答
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
于 2014-09-20T17:53:52.970 回答