7

我想以某种方式组合函数。请在伪代码(不是 F#)中考虑这两个函数

F1 = x + y
F2 = F1 * 10 // note I did not specify arguments for F1, 'reverse curry' for lack of a better word

我想让 F# 做的是弄清楚,因为

let F1 x y = x + y
//val F1 : int -> int -> int

该代码let F2 = F1 * 10会给我与 F1: 相同的签名val F2 : int -> int -> int,并且调用F2 2 3将导致 50: (2 + 3) * 10。这将是相当聪明的......

发生的事情完全不同。第一行按预期进行:

let F1 x y = x + y
//val F1 : int -> int -> int

但是当我添加第二行时let F2 = F1 * 10,它会抛出 F#。它现在抱怨那个the type int does not match the type 'a -> 'b -> 'c和那个 F1 requires member ( + )

我当然可以这样拼写:

let F1(x, y) = x + y
let F2(x, y) = F1(x, y) * 10

但现在我还不如使用 C#,我们不再那么遥远了。元组参数破坏了 F# 的许多优雅。此外,我的真实函数 F1 和 F2 的参数远不止 2 个,所以这让我眼花缭乱,这正是我想通过使用 F# 来躲避的。像这样说会更自然:

let F1 x y = x + y
let F2 = F1 * 10

有什么办法我可以(几乎)做到这一点?

额外积分:这些错误消息到底发生了什么?为什么第二行let F2 = F1 * 10会改变第一行的打字?

提前感谢您的想法,

格特-扬

更新 两个(几乎)做所描述的事情。

一个使用元组。第二行看起来有点古怪,工作正常。小缺点是我现在不能使用柯里化,否则我将不得不添加更多古怪的代码。

let F1 (a, b) = a + b
let F2 = F1 >> (*) 10

F2(2, 3) // returns 50

另一种方法是使用记录。乍一看,这更直接,更容易理解,但需要更多的代码和仪式。确实去掉了 F# 的一些优雅,看起来更像 C#。

type Arg (a, b) =
    member this.A = a
    member this.B = b

let F1 (a:Arg) = a.A + a.B
let F2 (a:Arg) = F1(a) * 10

F2 (Arg(2, 3)) // returns 50
4

3 回答 3

5

一般来说,没有这种模式。使用 larsmans 建议的组合符(如curryuncurry)是一种选择,但我认为结果比显式版本更不可读且更长。

如果你经常使用这个特定的模式,你可以定义一个运算符来将一个函数(有两个参数)乘以一个标量:

let ( ** ) f x = fun a b -> (f a b) * x

let F1 x y = x + y
let F2 = F1 ** 10

不幸的是,您不能将标准数字运算符(*等)的实现添加到现有类型(例如'a -> 'b -> int)中。但是,这是非常频繁的请求(它对其他事情很有用)。或者,您可以将函数包装到提供重载数字运算符的某个对象中(并包含一些Invoke用于运行该函数的方法)。

我认为一个合适的名称是提升 - 你正在提升*运算符(处理整数)到一个适用于返回整数的函数的版本。*当您使用可空类型时,它类似于在 C# 编译器中完成的提升。

解释错误消息 - 它抱怨表达式F1 * 10

错误 FS0001:类型 'int' 与类型 ''a -> 'b -> 'c' 不匹配

我认为这意味着编译器正在尝试为*运算符找到一个实例。从右侧,它计算出这应该是int,所以它认为左侧也应该是int- 但它实际上是两个参数的函数 - 类似于'a -> 'b -> c'

于 2011-05-04T12:23:30.367 回答
4

那会比较聪明...

太聪明了,它会打败类型系统。您想要的是 APL 中的数组编程。

有什么办法我可以(几乎)做到这一点?

我不会说 F#,但在 Haskell 中,你会 uncurry F1,然后 compose with *10,然后 curry:

f2 = curry ((*10) . uncurry f1)

在 F# 等 ML 方言中,它变成了这样:

let curry f x y = f (x,y)
let uncurry f (x,y) = f x y

let mult x y = x * y

let F1 x y = x + y
let F2 = curry (uncurry F1 >> mult 10)

(我不确定是否curryuncurry是否在 F# 标准库中,所以我定义了它们。也可能有一种更漂亮的方式来进行部分应用*而不定义mult.)

于 2011-05-04T12:08:04.547 回答
1

顺便说一句,使用无点(或者在这种情况下毫无意义)方法可以通过以下方式定义这些函数:

let F1 = (+)
let F2 = (<<)((*)10) << F1
于 2011-05-06T13:58:05.020 回答