← На главную

Научился использовать «батарейки»

Помните, как в крайнем посте я обмолвился, что в OCaml есть генераторы списков, но для их получения нужно произвести дополнительные действия, и в результате использовал указатель на голову списка с циклами for? Настало время выяснить, что это за дополнительные действия такие.

Пример использования генераторов списков в OCaml:

open Batteries_uni let string_of_list to_string lst = "[" ^ String.concat "; " (List.map to_string lst) ^ "]" let main () = let lst = [? List: x | x <- 1 -- 10; x mod 2 == 1 ?] in print_endline (string_of_list string_of_int lst) let _ = main ()

В действительности, конструкцию [? ... ?] неправильно называть генератором списков, поскольку это генератор чего угодно. Например, так можно сгенерировать множество букв латинского алфавита:

[? Set: x | x <- 'a' --~ 'z' ?]

Чтобы получить такое расширение синтаксиса, нам предстоит воспользоваться OCaml Batteries Included (или просто «батарейками»). Это поддерживаемый и развиваемый сообществом OCaml-программистов обширный набор хорошо документированных и согласованных между собой библиотек, а также расширений синтаксиса OCaml. Насколько я могу судить, в наши дни батарейки де-факто являются стандартной платформой для разработки программ на OCaml.

В Debian есть готовый пакет ocaml-batteries-included, который мы установили в прошлый раз. Также нам понадобится система сборки OMake:

sudo aptitude install omake omake-doc

Во FreeBSD, как обычно, все намного интереснее:

pkg_add -r ocaml-findlib ocaml-camomile omake

За неимением готового пакета, под FreeBSD батарейки придется собрать руками:

git clone git://github.com/ocaml-batteries-team/batteries-included.git cd batteries-included git checkout v1.5.0 gmake

Совсем недавно (25.01.13) вышли батарейки версии 2.0. Однако они зависят от bisect, который под FreeBSD также нужно собирать руками, предварительно забрав исходники из Darcs-репозитория. Думаю, на первое время вполне хватит проверенных временем батареек версии 1.5. Для их установки под рутом говорим:

gmake install gmake install-doc

Как в Debian, так и во FreeBSD, для использования батареек нам понадобится файл ~/.ocamlinit:

wget https://raw.github.com/ocaml-batteries-team/batteries-included/⏎ master/ocamlinit -O ~/.ocamlinit

Если теперь запустить интерпретатор OCaml, мы должны увидеть примерно следующее:

Objective Caml version 3.12.1 _________________________ [| + | | Batteries - | |_____|_|_________________| _________________________ | - Type '#help;;' | | + |] |___________________|_|___| Loading syntax extensions... Camlp4 Parsing version 3.12.1 #

Попробуем выполнить нашу программу:

# #use "test.ml";; val string_of_list : ('a -> string) -> 'a list -> string = <fun> val main : unit -> unit = <fun> [1; 3; 5; 7; 9] - : unit = () # main ();; [1; 3; 5; 7; 9] - : unit = () # #quit;;

Интерпретатор – это, конечно, здорово, но как нам скомпилировать программу, использующую батарейки? Тут в дело вступает OMake. Переходим в каталог с исходным кодом программы и выполняем команду:

omake --install

В результате будут созданы файлы OMakeroot и OMakefile. Первый можно оставить без изменений, а во второй следует прописать:

USE_OCAMLFIND = true OCAMLPACKS[] += batteries.pa_comprehension.syntax batteries.pa_string.syntax batteries OCAMLDEPFLAGS += -syntax camlp4o OCAMLFLAGS += -syntax camlp4o # имя программы PROGRAM = test # какие файлы компилируем FILES[] = test OCamlProgram($(PROGRAM), $(FILES)) .DEFAULT: $(if $(BYTE_ENABLED), $(PROGRAM).run) \ $(if $(NATIVE_ENABLED), $(PROGRAM).opt) .PHONY: clean clean: rm -f \ $(filter-proper-targets $(glob $(addsuffix .*, $(FILES)))) \ $(PROGRAM).run $(PROGRAM).opt

Еще раз проверяем, что не забыли прописать в исходном коде программы:

open Batteries_uni

… иначе при компиляции мы получите мистическую ошибку:

Error: Unbound value List.of_enum

Затем просто говорим omake и получаем исполняемый файл test.opt.

Что же делает omake? Давайте запустим его с флагом --verbose и выясним:

*** omake: reading OMakefiles *** omake: finished reading OMakefiles (0.01 sec) - build . test.o + ocamlfind ocamlopt -package batteries.pa_comprehension.⏎ syntax,batteries.pa_string.syntax,batteries -warn-error A ⏎ -syntax camlp4o -I . -c test.ml - exit . test.o, 0.18 sec, code 0 - build . test.opt + ocamlfind ocamlopt -package batteries.pa_comprehension.⏎ syntax,batteries.pa_string.syntax,batteries -warn-error A ⏎ -syntax camlp4o -I . -o test.opt test.cmx -linkpkg - exit . test.opt, 0.33 sec, code 0 *** omake: done (0.53 sec, 0/1 scans, 2/4 rules, 4/61 digests)

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

Помимо генераторов списков, батарейки предлагают много других вкусняшек, среди которых на данный момент я знаю и понимаю от силы процентов десять. Потому, если вас интересует сей вопрос, советую обратиться к официальной Wiki.

Дополнение: См также решение задачи про игру «кошки-мышки» на OCaml.