List.map с несколькими параметрами в F # - PullRequest
0 голосов
/ 30 апреля 2018

Это, пожалуй, проще всего объяснить на примере.

Допустим, у меня есть эта довольно многословная функция, которая увеличивает целое число x до nth power.

let powInt = function
| x,n -> 
  let rec loop acc n =
   match n with
   | 0 -> acc
   | v -> loop (acc * x) (v-1)
  loop 1 n

Если бы я хотел теперь поднять каждое целое число в списке целых чисел до степени nth , я подумал, что классная функция List.map в F # будет подходить :

let powIntList (xs: int list) = List.map powInt xs

Однако в последнем фрагменте в качестве аргумента в отображении отсутствует сила n , в то время как int x неявно извлекается функцией map.

Как добавить аргумент мощности n в этом примере?

1 Ответ

0 голосов
/ 30 апреля 2018

Вы делаете свою функцию карри.

Это как раз тот случай, когда curry , который является техникой для формулирования ваших функций таким образом, чтобы они принимали параметры, так сказать, «один за другим». Математически такая функция принимает ровно один параметр и возвращает другую функцию, которая принимает второй параметр. Как то так:

let curriedPowInt = fun n -> fun x -> powInt (x, n)

Этот способ определения функций является обычным явлением. На самом деле, он настолько важен для самой природы ML-языков (из которых F #), что для него существует специальный синтаксис:

let curriedPowInt n x = powInt (x, n)

Посмотрите, как я просто перечисляю свои параметры в одной строке, разделяя их пробелом, например n x =? Это синтаксический сахар для fun n -> fun x ->. Логически он определяет функцию, которая принимает параметры «один за другим», а не оба сразу как кортеж.

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

let pow2 = curriedPowInt 2

Это работает, потому что - помните? - моя функция curriedPowInt принимает n и возвращает другую функцию, которая принимает x. Тип pow2 теперь int -> int. Это функция, которая принимает int и повышает его до второй степени.

И, конечно, вы также можете использовать этот трюк:

let powIntList (xs: int list) = List.map (curriedPowInt n) xs

Также обратите внимание, как я изменил порядок параметров. Сначала я поставил параметр n, а последним параметр x. Это общее правило, делающее ваши функции более полезными: ваш «самый важный» параметр ставится последним, а «наименее важный» - первым. Например, посмотрите на List.map: список, над которым он работает, является последним аргументом двух.

Наконец, вместо объявления powInt, а затем отдельно curriedPowInt, я рекомендую вообще не объявлять первое. Просто сделайте все ваши функции карри. Функции карри гораздо полезнее. На самом деле, я так редко видел случаи, когда неиспользуемые функции были бы полезны, и с таким же успехом никогда не видел.

Итак, подведем итог:

let powInt n x = 
  let rec loop acc n =
   match n with
   | 0 -> acc
   | v -> loop (acc * x) (v-1)
  loop 1 n

let powIntList (xs: int list) = List.map (powInt n) xs
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...