F # передача оператора с аргументами в функцию - PullRequest
10 голосов
/ 30 ноября 2009

Можете ли вы передать операцию типа «делить на 2» или «вычесть 1», используя только частично примененный оператор, где «добавить 1» выглядит следующим образом:

List.map ((+) 1) [1..5];;  //equals [2..6]
// instead of having to write: List.map (fun x-> x+1) [1..5]

То, что происходит: 1 применяется к (+) в качестве первого аргумента, а элемент списка применяется в качестве второго аргумента. Для сложения и умножения этот порядок аргументов не имеет значения.

Предположим, я хочу вычесть 1 из каждого элемента (вероятно, это будет распространенная ошибка новичков):

List.map ((-) 1) [1..5];;  //equals [0 .. -4], the opposite of what we wanted

1 применяется к (-) в качестве первого аргумента, поэтому вместо (list_item - 1) я получаю (1 - list_item). Я могу переписать его как добавление отрицательного вместо вычитания положительного:

List.map ((+) -1) [1..5];;
List.map (fun x -> x-1) [1..5];; // this works too

Я ищу более выразительный способ написать что-то вроде ((-) _ 1), где _ обозначает заполнитель, как в языке Arc. Это приведет к тому, что 1 будет вторым аргументом -, поэтому в List.map он будет иметь значение list_item - 1. Поэтому, если вы хотите отобразить divide by 2 в список, вы можете написать:

List.map ((/) _ 2) [2;4;6] //not real syntax, but would equal [1;2;3] 
List.map (fun x -> x/2) [2;4;6] //real syntax equivalent of the above

Можно ли это сделать или мне нужно использовать (fun x -> x/2)? Кажется, что ближе всего к синтаксису-заполнителю мы можем использовать лямбду с именованным аргументом.

Ответы [ 3 ]

13 голосов
/ 30 ноября 2009

Вы могли бы написать функцию отражения, что-то вроде:

let flip f x y = f y x

List.map (flip (-) 1) [2;4;6]

Возможно, у меня неправильный синтаксис, я не очень хорошо говорю на F #.

10 голосов
/ 30 ноября 2009

В F # нет ни «секций операций», ни аскел, ни аргументов-заполнителей (очевидно, а-ля Арк). Вы можете использовать комбинатор 'flip', как предложено в другом ответе, чтобы изменить порядок аргументов, а затем частично применить первый (теперь второй) аргумент.

Но я бы просто использовал

fun x -> x / 2

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

9 голосов
/ 30 ноября 2009

Решение flip, предложенное Логаном Капальдо , также может быть записано с использованием оператора (здесь >.):

let (>.) x f = (fun y -> f y x)
List.map (1 >. (-)) [2;4;6]

Или, если вы предпочитаете операнды, наоборот:

let (>.) f x = (fun y -> f y x)
List.map ((-) >. 1) [2;4;6]

Редактировать: Использование оператора, который «больше похож на заполнитель» (здесь >-<), очень приближает вас к предлагаемому синтаксису:

List.map ((-) >-< 1) [2;4;6]

'_' к сожалению (?) Не является действительным символом оператора в F # .

...