Подтвердить что ты не робот

Когда мне следует писать свои функции в карри?

Недавно я начал изучать F # и сталкивался с карьерами для простых примеров, таких как:

Рассмотрим функцию, которая вычисляет продажи, умножая цену p на количество проданных единиц n.

let sales (p,n) = p * (float n);;

Тип этой функции задается как

val sales : p:float * n:int -> float

то есть. возьмите пару float и int и верните a float.

Вместо этого мы можем написать это как карриную функцию

let salesHi p n = p * (float n);;

Тип этой функции задается как

val salesHi : p:float -> n:int -> float

то есть. принимает значение float и возвращает функцию от int до float.

В простых случаях это, по-видимому, не имеет значения

sales (0.99, 100);;
salesHi 0.99 100;;

Оба дают

val it : float = 99.0

Однако с помощью функции curried я могу указать цену для определенных элементов для получения новых функций. Например.

let salesBeer =  salesHi 5.99;;
let salesWine =  salesHi 14.99;;

Тогда salesBeer 2 дает 11.98 и salesWine 2 дает 29.98.

Кроме того, я заметил, что встроенные операторы, такие как +, определяются как функции, поэтому я могу написать, например:

let plus2 = (+) 2;
List.map plus2 [1;3;-1];;

и получим

val it : int list = [3; 5; 1]

Это похоже на хорошую вещь. Поэтому, когда я хочу реализовать функцию на императивном языке, который бы принял аргументы n > 1, должен ли я, например, всегда использовать функцию curries в F # (если аргументы независимы)? Или я должен использовать простой маршрут и использовать регулярную функцию с помощью n -tuple и curry позже, если это необходимо? Или что-то еще?

Как программисты F # решают, когда делать функцию в карри или использовать регулярную функцию с кортежем?

4b9b3361

Ответ 1

Когда вы выбираете между картой и корневой формой, главное, чтобы рассмотреть, является ли кортеж, который вы принимаете в качестве аргумента, означает что-либо.

Заполненная форма. Например, float * float может представлять диапазон, и тогда рекомендуется использовать заполненную форму.

let normalizeRange (lo, hi) = if hi < lo then (hi, lo) else (lo, hi)
let expandRange by (lo, hi) = (lo - by, hi + by)

Хорошо, что вы можете создавать функции, которые работают на диапазонах. Например, вы можете написать что-то вроде:

randomRange() |> normalizeRange |> expandRange 10

Curried form.. С другой стороны, карри-форма - лучший выбор, если кортеж всех аргументов не является автономным значением с некоторым полезным значением. Например, силовая функция pown 2.0 10 - два аргумента - это количество и мощность, но маловероятно, что вы когда-либо использовали кортеж (2.0, 10) где-то еще в своей программе.

Карридная форма также полезна, когда у вас есть один "более важный" аргумент, потому что тогда вы можете использовать конвейерную обработку. Например, List.map должна быть указана так, чтобы это можно было сделать:

[1 .. 10] |> List.map (fun n -> n + 1)

Ответ 2

Или я должен взять простой маршрут и использовать регулярную функцию с n-кортежем и карри позже, если необходимо?

Чем проще найти работу? пусть материал x y z =... не просто меньше печатает, чем позволяет материалу (x, y, z), это на самом деле меньше работы, выполняемой языком. Вторая версия должна распределять кортеж, а затем разрушать кортеж в аргументы против первой версии, которая просто потребляет аргументы.

Карьерная форма - это идиоматический способ записи функций в F #. Я действительно не могу придумать веские основания использовать форму кортежа, если данные уже не были сохранены в виде кортежа.

Ответ 3

Еще одно соображение - если вы планируете взаимодействовать с С# или VB.NET, не беспокойтесь о валютной форме, так как они не очень хорошо знакомы с этими языками. С другой стороны, заполненная форма отображается как нормальный набор аргументов с точки зрения С#/VB.NET и очень естественна для потребления.

Ответ 4

Форма кортежа на самом деле немного опасна. Он может быть похож на языки в стиле ALGOL (= 90% популярных языков), но он работает по-другому.

Рассмотрим это:

foo bar(x,y)

Во всех языках в стиле ALGOL, которые допускают этот синтаксис, это означает "вызов bar с x и y в качестве аргументов и передать результаты в foo" - когда foo не имеет быть методом (это может быть синтаксис, например print в 2. * Python).

Однако в языках типа Lambda (например, ML, Haskell и F #) это означает "вызов foo с bar в качестве аргумента, а затем вызов результата (который является функцией) с кортежем (x,y) как аргумент.

Если вы новичок в языках в стиле лямбда, это может быть довольно запутанным. Теперь, если вы использовали форму карри, эквивалент был бы следующим:

foo bar x y

что так же неправильно, как foo bar(x,y) - но не так запутанно! Даже программисты, не знакомые с языками в стиле лямбда, могут легко понять, что bar не является первой функцией, которая будет вызываться в foo bar x y. Ошибка сразу понятна.