简体   繁体   English

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

[英]Partial application in Haskell with multiple arguments

Given some function f(x1,x2,x3,..,xN) it is often useful to apply it partially in several places. 给定一些函数f(x1,x2,x3,..,xN),将它部分地应用于几个地方通常很有用。 For example, for N=3 we could define g(x)=f(1,x,3). 例如,对于N = 3,我们可以定义g(x)= f(1,x,3)。 However, the standard partial application in Haskell does not work this way and only allows us to partially apply a function by fixing its first arguments (because all functions actually only take one argument). 但是,Haskell中的标准部分应用程序不能以这种方式工作,只允许我们通过修复其第一个参数来部分应用函数(因为所有函数实际上只接受一个参数)。 Is there any simple way to do something like this: 有没有简单的方法来做这样的事情:

g = f _ 2 _
g 1 3

with output the value of f 1 2 3 ? 输出值为f 1 2 3 Of course we could do a lambda-function 当然我们可以做一个lambda函数

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

but I find this quite unreadable. 但我觉得这很难读。 For example, in Mathematica it works like this, which I find quite nice: 例如,在Mathematica中,它就像这样,我觉得很好:

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

with output f[1,2,3] . 输出f[1,2,3]

Edit: Maybe I should say somehting more about the motivation. 编辑:也许我应该更多地谈论动机。 I would like to use such partially applied functions in point-style compositions, ie, in expressions like this: 我想在点样式合成中使用这种部分应用的函数,即在这样的表达式中:

h = g. f _ 2 . k

to get h 3 = g(f(k(3),2)) . 得到h 3 = g(f(k(3),2))

你可以阅读这个问题上如何改变参数的顺序,然后使用部分应用程序,但真正做的,目前在Haskell的干净和清晰的方式就是直接:

g x y = f x 2 y

No, the simplest way is to define a lambda. 不,最简单的方法是定义一个lambda。 You can probably try and play with flip , but I doubt it would be cleaner and simpler than a lambda. 你可以试着玩flip ,但我怀疑它会比lambda更干净,更简单。 Especially for longer list of arguments. 特别是对于更长的参数列表。

The simplest (and canonical) way is to define a lambda. 最简单(和规范)的方法是定义一个lambda。 It is much more readable if you use meaningful argument names where possible 如果您在可能的情况下使用有意义的参数名称,则更具可读性

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

you can define your new function with lambda syntax 您可以使用lambda语法定义新函数

getGBPData = \from to -> getCurrencyData from to GBP

or without it 或没有它

getGBPData from to = getCurrencyData from to GBP

or you can use combinators, but I think this is quite ugly 或者你可以使用组合器,但我认为这很难看

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

There is no general way to do what you're asking, but you can sometimes use infix sections as an alternate to flip . 没有一般的方法来做你要求的,但你有时可以使用中缀部分作为flip的替代。 Using your last example: 使用你的上一个例子:

g . (`f` 2) . k

Also, I'd like to point out that sometimes it can help if you reorder the arguments your functions take. 此外,我想指出,如果你重新排序函数所采用的参数,它有时会有所帮助。 For instance, if you have a function that will often be partially applied to one argument in particular, you should probably make that the first argument. 例如,如果你有一个特别经常部分应用于一个参数的函数,你应该把它作为第一个参数。

Say you are implementing a data structure that represents a 2D game board (like you might for a Chess program), you would probably want the first argument to your getPiece function to be a Chess board and the second argument to be the location. 假设您正在实现一个代表2D游戏板的数据结构(就像您可能用于Chess程序一样),您可能希望getPiece函数的第一个参数是Chess板,第二个参数是该位置。 I think it is likely that the location being checked would be changed more often than the board. 我认为被检查的位置可能会比董事会更频繁地更改。 Of course, this doesn't fix the general issue (maybe you want to check the same location in a list of boards) but it can alleviate it. 当然,这并不能解决一般问题(也许你想检查一块板上的相同位置),但它可以减轻它。 When I decide on an argument order, this is the main thing I consider. 当我决定一个参数顺序时,这是我考虑的主要问题。

Other things to consider: 其他需要考虑的事项:

Define helper functions for the partial application patterns (either locally, or globally if you find yourself using a small number of patterns several times). 为部分应用程序模式定义辅助函数(本地或全局,如果您发现自己多次使用少量模式)。

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

Not quite as nice as your hypothetical "blank" syntax, but okay; 不像你假设的“空白”语法那么好,但没关系; you can read fix_2 as a tag identifying the partial application scheme in use 1 . 您可以将fix_2读作标识正在使用的部分应用程序方案1的标记。

Note that you never need any partial application schemes that don't fix the last argument; 请注意,您永远不需要任何不修复最后一个参数的部分应用程序方案; with currying fixing the first and third arguments of a 4 argument function (leaving a two argument function) is the same as fixing the first and third argument of a 3 argument function. 用currying修复4参数函数的第一个和第三个参数(留下两个参数函数)与修复3参数函数的第一个和第三个参数相同。

As a more involved idea, I believe you should be able to write a Template Haskell quasiquoter that actually implements your "underscores are parameters of an implied function" pseudosyntax. 作为一个更复杂的想法,我相信你应该能够编写一个模板Haskell quasiquoter,它实际上实现了你的“下划线是一个隐含函数的参数”pseudosyntax。 This would allow you to write expressions lie this: 这将允许你写表达式谎言:

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

More syntactic overhead than a helper function, and you still need to involve an additional name (to identify the quasiquoter), but perhaps easier to read if your partial application scheme is much more complicated: 比辅助函数更多的语法开销,你仍然需要一个额外的名称(以识别quasiquoter),但如果您的部分应用程序方案更复杂,可能更容易阅读:

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

It means a bunch of work implementing the Template Haskell code though, which I've never done so no idea how much. 这意味着实现模板Haskell代码的一大堆工作,我从来没有做过,所以不知道多少。 But then you'd have arbitrary partial application schemes, whereas the helper function route requires a manual definition for each pattern. 但是你有任意的部分应用程序,而辅助函数路由需要为每个模式手动定义。


1 Note that fixing only the second argument already has a standard helper function: flip . 1请注意,仅修复第二个参数已经具有标准辅助函数: flip Partially applying to the second argument might not intuitively sound like swapping the first two arguments, but thanks to lazy evaluation and currying they are actually the same thing! 部分适用于第二个参数可能听起来不像是交换前两个参数,但是由于懒惰的评估和讨论,它们实际上是同一个东西!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM