Я хочу создать список целых чисел от 1 до n. Я могу сделать это в Python, используя диапазон (1, n + 1) и в Haskell, используя: take n (итерация (1+) 1).
Какая правильная идиома OCaml для этого?
Я хочу создать список целых чисел от 1 до n. Я могу сделать это в Python, используя диапазон (1, n + 1) и в Haskell, используя: take n (итерация (1+) 1).
Какая правильная идиома OCaml для этого?
Нет идиомы, о которой я знаю, но это довольно естественное определение с использованием инфиксного оператора:
# let (--) i j =
let rec aux n acc =
if n < i then acc else aux (n-1) (n :: acc)
in aux j [] ;;
val ( -- ) : int -> int -> int list = <fun>
# 1--2;;
- : int list = [1; 2]
# 1--5;;
- : int list = [1; 2; 3; 4; 5]
# 5--10;;
- : int list = [5; 6; 7; 8; 9; 10]
В качестве альтернативы расширение расширений синтаксиса понятий (которое дает синтаксис [i .. j]
для вышеуказанного), скорее всего, будет включено в будущую версию "версия сообщества" OCaml, так что может стать идиоматическим. Я не рекомендую вам начинать играть с расширениями синтаксиса, если вы новичок в этом языке.
С Батареи включены, вы можете написать
let nums = List.of_enum (1--10);;
Оператор --
генерирует перечисление от первого значения ко второму. Оператор --^
аналогичен, но перечисляет полуоткрытый интервал (1--^10
будет перечислять от 1 до 9).
Здесь вы идете:
let rec range i j = if i > j then [] else i :: (range (i+1) j)
Обратите внимание, что это не хвостовая рекурсия. Современные версии Python даже имеют ленивый диапазон.
Это работает в базе OCaml:
# List.init 5 (fun x → x + 1);; -: int list = [1; 2; 3; 4; 5]
OCaml имеет специальный синтаксис для сопоставления шаблонов на диапазонах:
let () =
let my_char = 'a' in
let is_lower_case = match my_char with
| 'a'..'z' -> true (* Two dots define a range pattern *)
| _ -> false
in
printf "result: %b" is_lower_case
Чтобы создать диапазон, вы можете использовать Core
:
List.range 0 1000
Если вы используете open Batteries
(это версия сообщества стандартной библиотеки), вы можете сделать range(1,n+1)
на List.range 1 `To n
(обратите внимание на обратную кавычку перед To
).
Более общий способ (также нужен аккумулятор) - использовать List.init n f
, который возвращает список, содержащий (f 0) (f 1)... (f (n-1)).
Немного поздно в игре здесь, но здесь моя реализация:
let rec range ?(start=0) len =
if start >= len
then []
else start :: (range len ~start:(start+1))
Вы можете использовать его очень как функция python:
range 10
(* equals: [0; 1; 2; 3; 4; 5; 6; 7; 8; 9] *)
range ~start:(-3) 3
(* equals: [-3; -2; -1; 0; 1; 2] *)
Естественно, я считаю, что лучший ответ - просто использовать Core, но это может быть лучше, если вам нужна только одна функция, и вы пытаетесь избежать полной структуры.
Кстати, в Haskell вы предпочитаете использовать
enumFromTo 1 n
[1 .. n]
Это просто не нужно.
take n [1 ..]
take n $ iterate (+1) 1
Вслед за Алексом Ковентри сверху, но еще короче.
let range n = List.init n succ;;
> val range : int -> int list = <fun>
range 3;;
> - : int list = [1; 2; 3]
Если вам не нужен параметр "step", один из простых способов реализовать эту функцию:
let range start stop = List.init(abs @@stop - start) (fun я → я + start)
Придумали это:
let range a b =
List.init (b - a) ((+) a)