如何在 F# 或任何函数式语言中柯里化第二个(或第三个、第四个……)参数?
-
19-09-2019 - |
题
我刚刚开始使用 F#,看看如何使用柯里化将第一个参数预加载到函数中。但是如何使用第二个、第三个或任何其他参数来做到这一点呢?命名参数会让这变得更容易吗?是否有任何其他函数式语言具有命名参数或其他方式使柯里化与参数顺序无关?
解决方案
通常你只需使用lambda:
fun x y z -> f x y 42
像“F”,但与结合到42上的第三个参数的函数。
您也可以使用组合程序(比如有人提到在评论Haskell的“翻转”),该重新排序参数,但我有时觉得还不清楚。
请注意,大多数咖喱功能被写入,使得参数-最可能-待部分应用至上。
F#已命名方法的参数(未让结合函数值),但名称适用于“tupled”参数。命名令行禁止参数没有太大的意义;如果我有两个参数的函数咖喱“F”,我期望给定
let g = f
let h x y = f x y
然后“G”或“H”是可取代为“F”,而是“命名为”参数使这不一定是真实的。也就是说,“命名参数”可以用语言设计的其他方面不良相互作用,我个人不知道一个好的设计副手对于以“一流的咖喱函数值”交互得很好“命名参数”的。
其他提示
只是为了完整性 - 既然你问其他功能的语言 - 这是你会怎么做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
)使用其名称。
微软选择不执行这个功能,因为你发现了......在我的愚见,这不是关于“用语言设计的其他方面相互作用不良” ......这是因为额外的努力,这更可能将要求(在语言中实现)和延迟会导致使语言世界 - 事实上,只有少数人会(一)注意从OCaml中的“降压”的,(b)反正使用命名的函数参数
我是少数派,做使用它们 - 但它确实是一件在F#很容易模仿与本地功能绑定:
let foo x y = x - y
let bar x = foo x 3
bar ...
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
在Python,可以使用functools.partial
,或λ。 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)
可以在不声明任何内容的情况下执行此操作,但我同意 布莱恩 那 lambda 或自定义函数可能是更好的解决方案.
我发现我最常需要这个来部分应用除法或减法。
> 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)
或者,比较:
let someFloats = [0.0 .. 10.0]
let theFloatsDividedByFour1 = someFloats |> List.map ((/) >> (|>) 4.0)
let theFloatsDividedByFour2 = someFloats |> List.map (fun f -> f / 4.0)
lambda 版本似乎更容易阅读。