我刚刚开始使用F#,看看如何使用currying将第一个参数预加载到函数中。但是如何用第二,第三或其他任何参数来做呢?命名参数会使这更容易吗?是否还有其他任何具有命名参数的函数式语言或其他一些使得currying对参数顺序无关紧要的方法?
答案 0 :(得分:19)
通常你只使用lambda:
fun x y z -> f x y 42
是类似'f'的函数,但第三个参数绑定到42。
你也可以使用组合器(就像有人在评论中提到Haskell的“翻转”),它会重新排序参数,但我有时会觉得这很混乱。
请注意,大多数curried函数都是这样编写的,因此最有可能部分应用的参数首先出现。
F#命名了方法的参数(不是let-bound函数值),但这些名称适用于'tupled'参数。命名的咖喱参数没有多大意义;如果我有两个参数curried函数'f',我会期望给出
let g = f
let h x y = f x y
然后'g'或'h'可替代'f',但'named'参数使得这不一定是真的。也就是说,“命名参数”与语言设计的其他方面的交互性很差,而且我个人并不知道“命名参数”的良好设计与“一流的curried函数值”相互作用。 / p>
答案 1 :(得分:6)
只是为了完整 - 而且因为你问过其他功能语言 - 这就是你如何在OCaml中做到这一点,可以说是"母亲" F#:
$ ocaml
# let foo ~x ~y = x - y ;;
val foo : x:int -> y:int -> int = <fun>
# foo 5 3;;
- : int = 2
# let bar = foo ~y:3;;
val bar : x:int -> int = <fun>
# bar 5;;
- : int = 2
因此,在OCaml中,您可以使用其名称(上例中的y
)对您想要的任何命名参数进行硬编码。
微软选择不实施此功能,正如您所发现的那样......在我的拙见中,它并不是关于与语言设计的其他方面的不良交互&#34; ...它更有可能是因为这需要额外的努力(在语言实现中)以及它将语言带到世界的延迟 - 实际上只有少数人会(a)意识到&#34; stepdown& #34;来自OCaml,(b)无论如何都使用命名函数参数。
我是少数,并且确实使用它们 - 但它确实可以在F#中使用本地函数绑定轻松模拟:
let foo x y = x - y
let bar x = foo x 3
bar ...
答案 2 :(得分:5)
OCaml是F#所基于的语言,它具有可以按任何顺序指定的标记(和可选)参数,您可以根据这些参数的名称部分应用函数。我不相信F#有这个功能。
您可以尝试创建类似Haskell的flip
函数。创建在参数列表中进一步跳转参数的变体应该不会太难。
let flip f a b = f b a
let flip2 f a b c = f b c a
let flip3 f a b c d = f b c d a
答案 3 :(得分:4)
在Python中,您可以使用functools.partial
或lambda。 Python已命名参数。
functools.partial
可用于指定第一个位置参数以及任何命名参数。
from functools import partial
def foo(a, b, bar=None):
...
f = partial(foo, bar='wzzz') # f(1, 2) ~ foo(1, 2, bar='wzzz')
f2 = partial(foo, 3) # f2(5) ~ foo(3, 5)
f3 = lambda a: foo(a, 7) # f3(9) ~ foo(9, 7)
答案 4 :(得分:3)
可以在不声明任何内容的情况下执行此操作,但是我同意Brian认为a lambda or a custom function is probably a better solution。
我发现我最经常希望将其用于除法或减法的部分应用。
lags
为概括起见,我们可以声明一个函数> let halve = (/) >> (|>) 2.0;;
> let halfPi = halve System.Math.PI;;
val halve : (float -> float)
val halfPi : float = 1.570796327
:
applySecond
要遵循逻辑,可能有助于定义函数:
> let applySecond f arg2 = f >> (|>) arg2;;
val applySecond : f:('a -> 'b -> 'c) -> arg2:'b -> ('a -> 'c)
现在> let applySecond f arg2 =
- let ff = (|>) arg2
- f >> ff;;
val applySecond : f:('a -> 'b -> 'c) -> arg2:'b -> ('a -> 'c)
是从f
到'a
的函数。它由'b -> 'c
组成,这是一个从ff
到'b -> 'c
的函数,其功能是将'c
部分应用到正向管道运算符。此函数将为arg2
传递的特定'b
值应用于其参数。因此,当我们将arg2
与f
组合在一起时,我们得到一个从ff
到'a
的函数,该函数使用给定的'c
参数值我们想要的。
将上面的第一个示例与以下内容进行比较:
'b
还要比较这些:
> let halve f = f / 2.0;;
> let halfPi = halve System.Math.PI;;
val halve : f:float -> float
val halfPi : float = 1.570796327
或者,比较:
let filterTwoDigitInts = List.filter >> (|>) [10 .. 99]
let oddTwoDigitInts = filterTwoDigitInts ((&&&) 1 >> (=) 1)
let evenTwoDigitInts = filterTwoDigitInts ((&&&) 1 >> (=) 0)
let filterTwoDigitInts f = List.filter f [10 .. 99]
let oddTwoDigitInts = filterTwoDigitInts (fun i -> i &&& 1 = 1)
let evenTwoDigitInts = filterTwoDigitInts (fun i -> i &&& 1 = 0)
lambda版本似乎更易于阅读。