Обратный карри? - PullRequest
       13

Обратный карри?

7 голосов
/ 04 мая 2011

Я бы хотел составить функции определенным образом.Пожалуйста, рассмотрите эти 2 функции в псевдокоде (не 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 меняет набор текста на первой?

Заранее спасибо за ваши мысли,

Gert-Jan

update Два аплодисмента, которые (почти) делают то, что описано.

Один использует кортеж.Вторая строка выглядит немного странно, первая работает нормально.Небольшой недостаток в том, что я не могу использовать каррирование сейчас, или мне придется добавить еще более причудливый код.

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

Ответы [ 3 ]

5 голосов
/ 04 мая 2011

В общем, шаблонов для этого нет. Использование комбинаторов (таких как curry и uncurry), как предложено larsmans, является одним из вариантов, но я думаю, что результат менее читабелен и длиннее, чем явная версия.

Если вы часто используете этот конкретный шаблон, вы можете определить оператор для умножения функции (с двумя параметрами) на скаляр:

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'.

4 голосов
/ 04 мая 2011

Это было бы довольно умно ...

Настолько умный, что он, черт возьми, выбьет из системы типов. То, что вы хотите, это программирование массива как в APL.

Есть ли способ, которым я могу (почти) сделать это?

Я не говорю на F #, но в Хаскеле вы бы не спешили F1, затем сочиняли с *10, затем карри:

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

Который на диалекте ML, таком как F #, становится чем-то вроде:

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)

(Я не был уверен, есть ли curry и uncurry в стандартной библиотеке F #, поэтому я определил их. Также может быть более красивый способ сделать частичное применение * без определения mult. )

1 голос
/ 06 мая 2011

Кстати, используя бессмысленный (или, скорее, бессмысленный) подход, можно определить эти функции следующим образом:

let F1 = (+)
let F2 = (<<)((*)10) << F1
...