Необязательный аргумент не может быть удален?
-
13-09-2019 - |
Вопрос
Я хотел иметь хвостовую рекурсивную версию List.map
, поэтому я написал свой собственный.Вот оно:
let rec list_map f l ?(accum=[])=
match l with
head :: tail -> list_map f tail ~accum:(head :: accum)
| [] -> accum;;
Всякий раз, когда я компилирую эту функцию, я получаю:
File "main.ml", line 69, characters 29-31:
Warning X: this optional argument cannot be erased.
В Учебник говорит, что это означает, что я пытаюсь создать функцию без необязательных аргументов.Но приведенная выше функция явно принимает необязательные аргументы.
Наверное, я просто делаю что-то действительно глупое, но что?
Решение
Предыдущие решения действительно компилируются, но не дают ожидаемого результата.Функция f
никогда не применяется к аргументам.Правильный код - это:
let rec list_map f ?(accum = []) l = match l with
| head :: tail -> list_map f ~accum:(f head :: accum) tail
| [] -> accum;;
Предполагаемый тип является:
val list_map : ('a -> 'b) -> ?accum:'b list -> 'a list -> 'b list = <fun>
...в отличие от неправильного:
val list_map : 'a -> ?accum:'b list -> 'b list -> 'b list = <fun>
Пожалуйста, обратите внимание, что список результатов перевернут:
# list_map ( ( ** ) 2.) [1.;2.;3.;4.];;
- : float list = [16.; 8.; 4.; 2.]
...и равно функции rev_list из модуля списка:
# List.rev_map ( ( ** ) 2.) [1.;2.;3.;4.];;
- : float list = [16.; 8.; 4.; 2.]
Таким образом, вы можете захотеть изменить свою функцию на:
let rec list_map f ?(accum = []) l = match l with
| head :: tail -> list_map f ~accum:(f head :: accum) tail
| [] -> List.rev accum;;
...который также должен быть хвостово-рекурсивным (согласно руководству) и возвращает список в исходном порядке:
# list_map ( ( ** ) 2.) [1.;2.;3.;4.];;
- : float list = [2.; 4.; 8.; 16.]
Другие советы
Да, ваш необязательный аргумент не может быть последним, потому что, поскольку OCaml поддерживает частичные приложения, функция, в которой отсутствует последний необязательный аргумент, будет просто выглядеть как частично примененная функция, которая все еще ищет необязательный аргумент.Единственный способ сообщить ему, что вы не собираетесь предоставлять необязательный аргумент, - это увидеть, что вы предоставили аргумент после него.
Если вам нужно сделать это в последнюю очередь, вы можете поставить пустышку unit
аргумент после этого:
let rec list_map f l ?(accum=[]) () =
match l with
head :: tail -> list_map f tail ~accum:(head :: accum) ()
| [] -> accum;;
Но в этом случае, да, изменение порядка было бы лучше.
Вам нужен необязательный аргумент после необязательный вариант.Просто измените порядок аргументов вашей функции:
let rec list_map f ?(accum=[]) l=
match l with
head :: tail -> list_map f ~accum:(head :: accum) tail
| [] -> accum;;