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

11 февраля 2013

Помните, как в крайнем посте я обмолвился, что в 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.

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.