Идиоматический способ распечатать список вещей в OCaml - использовать стандартный модуль Format
, который, кстати, предоставляет функцию pp_print_list
, которая принимает принтер элементов, принтер-разделитель и списоки печатает список указанных элементов, например,
let pp_comma ppf () = Format.fprintf ppf ",@ "
let pp_int_list ppf ints =
Format.fprintf ppf "[%a]"
Format.(pp_print_list ~pp_sep:pp_comma pp_print_int) ints
# Format.printf "@[hello = [%a]@]@\n" pp_int_list [1;2;3];;
hello = [1, 2, 3]
Функция pp_print_list
имеет тип
?pp_sep:(formatter -> unit -> unit) ->
(formatter -> 'a -> unit) ->
(formatter -> 'a list -> unit)
Т.е. это функция, которая принимает две функции (одна из которыхнеобязательно) и возвращает функцию, которая печатает список. Если мы опускаем параметр pp_sep
для краткости, то мы можем сказать, что pp_print_list
берет принтер 'a
и возвращает принтер 'a list
, где принтер является функцией типа formatter -> 'a -> unit
. Функция pp_print_list
- это так называемая функция высшего порядка, поскольку она принимает в качестве параметра другую функцию. В функциональном программировании в целом и в OCaml в частности функции высшего порядка очень распространены, поэтому никто не делает из этого большого дела.
Поскольку в OCaml нет никаких средств для самоанализа, а информация о типе стираетсяво время компиляции в модуле нет общих средств для красивой печати, кроме модуля Format
. Поэтому для каждого вновь определенного типа мы должны предоставить принтер, т. Е. Функцию типа formatter -> t -> unit
, где t
- наш новый тип. Это идиома, общая для многих статически скомпилированных языков, ср. с классом Show
Haskell или реализацией функции <<
в C ++. В OCaml нет классов типов или перегрузки, поэтому нет официального или канонического имени для функции принтера, но существует соглашение называть такую функцию pp
, cf Int.pp
и Float.pp
и т. Д. В ядре Janestreet. библиотека.
Если мы вернемся к вашему решению, то будет легко увидеть, что printListBrec
и prListIntrec
в основном одинаковы, они отличаются только способом печати элементов. Поэтому мы можем параметризовать такую функцию с помощью функции, которая принимает значение типа элемента списка и, например, переводит его в строковый тип, например,
let rec prGen prItem l =
match l with
[] -> Printf.printf "]\n"
| g :: []-> Printf.printf "%s]\n" (prItem g) g
| g :: t -> Printf.printf "%s; " (prItem g); prGen prItem t
Эта функция уже будет более общей,хотя немного не идиоматично. Поскольку перевод в строку, а затем печать не очень эффективны (зачем создавать промежуточный объект, если мы можем напечатать его напрямую), мы используем принтеры и спецификаторы %a
(описание соответствующих принтеров см. В соответствующих модулях Printf и Format),Наконец, мы не используем camelCase в OCaml (несмотря на то, что OCaml - это OCaml).