反向咖啡? [英] Reverse currying?

查看:97
本文介绍了反向咖啡?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想以某种方式编写函数。请考虑伪代码中的这两个函数(而不是F#)

pre $ code F1 = x + y
F2 = F1 * 10 / / note我没有为F1指定参数,'reverse curry'缺少更好的单词

因为

 让F1 xy = x + y 
// val F1:int - > int - > int

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

会发生什么是完全不同的寿命。第一行如预期:

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

但是当我添加第二行让F2 = F1 * 10 它抛出F#。它抱怨类型int不匹配类型'a - > 'b - > 'c ,现在F1 需要member(+)



我可以当然这样拼出来:

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

但是现在我不妨使用C# ,我们不再那么遥远了。 tupled参数打破了F#的许多优雅。另外我的实际功能F1和F2比2的参数要多得多,所以这让我变得十分注意,正是我想用F#躲避的。说这样会更自然:

 让F1 xy = x + y 
让F2 = F1 * 10

有没有什么办法可以(几乎)这样做?



额外的信用:这些错误信息到底发生了什么?为什么第二行让F2 = F1 * 10 改变了第一个键入?

您的想法,

Gert-Jan

更新两个应用程序(几乎)做所描述的。



一个使用元组。第二行看起来有点古怪,工作正常。小缺点是我现在无法使用currying,或者我不得不添加更古怪的代码。

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

F2(2,3)//返回50

另一种方法是使用记录。这是一个更直接,更容易乍一看,但需要更多的代码和仪式。删除F#的一些优雅,看起来更像C#。

  type Arg(a,b)= 
(a:Arg)= aA + aB
让F2(a:Arg)= F1(a )* 10

F2(Arg(2,3))//返回50


解决方案

一般来说,没有这种模式。 larsmans建议使用combinators(如 curry uncurry )是一种选择,但我认为结果是可读性较差并且比明确的版本更长。



如果您经常使用这种特殊模式,您可以定义一个运算符,用于将函数(使用两个参数)乘以标量: p>

  let(**)fx = fun ab  - > (fab)* x 

让F1 xy = x + y
让F2 = F1 ** 10

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



我认为一个合适的名称将会解除 - 你将 * 运算符(对整数进行处理)解压到一个对返回整数的函数起作用的版本。这与使用 * 来处理可空类型时在C#编译器中完成的提升类似。



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


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


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


I'd like to compose functions in a certain way. Please consider these 2 functions in pseudocode (not F#)

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

What I would like for F# to do is figure out that since

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

the code let F2 = F1 * 10 would give me the same signature as F1: val F2 : int -> int -> int, and calling F2 2 3 would result in 50: (2 + 3) * 10. That would be rather clever...

What happens is quite different tho. The first line goes as expected:

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

but when I add a second line let F2 = F1 * 10 it throws off F#. It complains that the type int does not match the type 'a -> 'b -> 'c and that F1 now requires member ( + ).

I could of course spell it out like this:

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

But now I might as well have used C#, we're not that far away anymore. The tupled arguments break a lot of the elegance of F#. Also my real functions F1 and F2 have a lot more arguments than just 2, so this makes me go cross eyed, exactly what I wanted to dodge by using F#. Saying it like this would be much more natural:

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

Is there any way I can (almost) do that?

For extra credits: what exactly goes on with these error messages? Why does the second line let F2 = F1 * 10 change the typing on the first?

Thanks in advance for your thoughts,

Gert-Jan

update Two apporaches that (almost) do what's described.

One using a tuple. Second line looks a little quirky a first, works fine. Small drawback is I can't use currying now or I'll have to add even more quirky code.

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

F2(2, 3) // returns 50

Another approach is using a record. That is a little more straight forward and easier to get at first glance, but requieres more code and ceremony. Does remove some of the elegance of F#, looks more like 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

解决方案

There is no pattern for this in general. Using combinators (like curry and uncurry) as suggested by larsmans is one option, but I think the result is less readable and longer than the explicit version.

If you use this particular pattern often, you could define an operator for multiplying a function (with two parameters) by a scalar:

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

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

Unfortunately, you cannot add implementation of standard numeric operators (*, etc.) to existing types (such as 'a -> 'b -> int). However, this is quite frequent request (and it would be useful for other things). Alternatively, you could wrap the function into some object that provides overloaded numeric operators (and contains some Invoke method for running the function).

I think an appropriate name for this would be lifting - you're lifting the * operator (working on integers) to a version that works on functions returning integers. It is similar to lifting that is done in the C# compiler when you use * to work with nullable types.

To explain the error message - It complains about the expression F1 * 10:

error FS0001: The type 'int' does not match the type ''a -> 'b -> 'c'

I think it means that the compiler is trying to find an instantiation for the * operator. From the right-hand side, it figures out that this should be int, so it thinks that the left-hand side should also be int - but it is actually a function of two arguments - something like 'a -> 'b -> c'.

这篇关于反向咖啡?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆