пятница, 25 марта 2011 г.

Clojure как средство изучения java-библиотек


Вообще, java-библиотеки осваивают, как правило, именно на java. Традиционный цикл выглядит так:

  • написать пример использования какой-нибудь функции;
  • скомпилировать, исправить синтаксические ошибки;
  • скомпилировать, убедиться, что ничего не работает;
  • опять поправить использование функций в соответствии с документацией;
  • скомпилировать, получить какой-то результат.
Весь цикл повторить для исследования каждой новой возможности.

Недавно мне пришлось разбираться с библиотекой libsvm -- реализацией алгоритма классификации по методу машины опорных векторов. Оказалось, что clojure подходит для исследования возможностей java-библиотек гораздо лучше, чем сама java. Дело, конечно же, в repl-е. Удобство состоит в том, что выполнив какую-то часть вычислений, можно продолжать экспериментировать с результатами. Например, выполнив классификацию на первом тестовом наборе, можно тут же, не сходя с места, подправить определение тестового набора и запустить классификацию на другом и т.д. Процесс идет гораздо быстрее, потому что для исследования каждой отдельной функции библиотеки совсем не обязательно перезапускать весь цикл исправление-компиляция-выполнение.
Еще одна приятная возможность из библиотеки clojure-contrib -- функция show. Она показывает все методы, принадлежащие переданные ей сущности. Для использования show ее нужно импортировать: (use '[clojure.contrib.repl-utils :only (show)]). Выход функции выглядит так:
user=> (show 10)
===  public final java.lang.Integer  ===
[ 0] static MAX_VALUE : int
[ 1] static MIN_VALUE : int
[ 2] static SIZE : int
[ 3] static TYPE : Class
[ 4] static bitCount : int (int)
[ 5] static decode : Integer (String)
[ 6] static getInteger : Integer (String)
[ 7] static getInteger : Integer (String,Integer)
[ 8] static getInteger : Integer (String,int)
[ 9] static highestOneBit : int (int)
[10] static lowestOneBit : int (int)
[11] static numberOfLeadingZeros : int (int)
[12] static numberOfTrailingZeros : int (int)
[13] static parseInt : int (String)
[14] static parseInt : int (String,int)
[15] static reverse : int (int)
[16] static reverseBytes : int (int)
[17] static rotateLeft : int (int,int)
[18] static rotateRight : int (int,int)
[19] static signum : int (int)
[20] static toBinaryString : String (int)
[21] static toHexString : String (int)
[22] static toOctalString : String (int)
[23] static toString : String (int)
[24] static toString : String (int,int)
[25] static valueOf : Integer (String)
...
Правда работает она пока что только в стабильной (1.2.0) ветке Clojure. В тестовой 1.3.0-alpha4 сваливается с ошибкой.

9 комментариев:

  1. Хм, а я уже давно обнаружил, что аналогичные утверждения также верны и для изучения C-библиотек с помощь Common Lisp )

    ОтветитьУдалить
  2. О, прикольно!

    Кстати, как твой Mongrel2 + CL? Есть уже что-нибудь, чтоб можно скачать и пощупать? Я пробовал установить, как написано на их хомсайте, но не осилил зависимости...

    ОтветитьУдалить
  3. @Dmitry

    На Mongrel2 я наступаю набегам ) После последнего я смог запустить локальную версию lisper.ru на основе Mongrel2 без Hunchentoot. Правда с тем ограничением, что вся обработка идёт в однопоточном режиме, плюс оверхед большой (на копирование памяти туда/сюда).

    Mongrel2 для меня это сейчас работа на перспективу, так что я не форсирую, а доступного времени мало, ибо из-за скудности имеющихся библиотек очень много более актуальных задач (

    Кстати, сам Mongrel2 ставится то тривиально (make && make install), но он требует свежие версии используемых библиотек, что на каком-нибудь Debian может оказаться не очень удобным. Ну да у меня Gentoo, поэтому никаких проблем не было.

    Скачать/пощупать можно попробовать, но это требует некоторых усилий и сопровождения с моей стороны.

    ОтветитьУдалить
  4. jruby именно для этого ещё удобнее

    ОтветитьУдалить
  5. А как там repl интегрируется с текстовым редактором? С тем же удобством, что emacs/slime? И еще, как там доступ к java-типам? Насколько он прост?

    ОтветитьУдалить
  6. ruby-1.9.2-p0 > java.lang.Integer.methods.sort
    => ["<", "<=", "<=>", "==", "===", "=~", ">", ">=", "[]", "__id__", "__jtrap", "__send__", "allocate", "ancestors", "autoload", "autoload?", "bitCount", "bitCount__method", "bit_count", "bit_count__method", "class", "class_eval", ...
    Тут он конечно подмешивает ruby-specific methods.
    ruby-1.9.2-p0 > java.lang.Integer.methods.grep /get/
    => ["getInteger", "get_integer", "get_integer__method", "getInteger__method", "const_get", "instance_variable_get"]
    Если подскажете подобное в clojure repl буду очень признателен потому что часто необходимо.
    Я Emacs к большому сожалению не владею, вроде там есть ruby mode, насколько он хорош не знаю.

    ОтветитьУдалить
  7. Ну так я же написал, там есть функция show, которая выводит все методы для объекта. В статье -- перечислены первые 26 методов класса Integer; примесей вроде никаких нету. Впрочем, не это самое главное, Intellisence худо-бедно умеют все IDE. Но вот интеграции с нормальным repl-ом я еще нигде не видел, кроме emacs+slime. Хотя Eclipse и Netbeans, похоже, идут в этом направлении.

    ОтветитьУдалить
  8. show *печатает* методы объекта. .methods в руби возвращает массив имён методов. У рубиевского Array есть такие удобные функции как например sort и grep
    Мне это помогает в репл посмотреть какие например get методы есть у java класса

    ОтветитьУдалить
  9. Посмотрел (source show), переделаю чтобы возвращал sequence :)

    ОтветитьУдалить