Not so far, I found interesting resource, which contains tips for different programming languages. Here is the one for the clojure:
Wednesday, April 1, 2015
Sunday, July 7, 2013
Emacs как Сlojure IDE
Во время изучения нового языка программирования вопрос об IDE стоит не на последнем месте. Я предпочитаю, когда рабочее окружение строится вокруг привычного инструментария, доставил плагин - получаешь необходимый функционал. Обычно это работает. Но только не с clojure. dev.clojure.org предлагает множество вариантов, но действительно рабочими являются немногие. До недавнего времени использовал Counterclockwise, но как показала практика, решение не самое удобное и стабильное. Плагин для IntelliJ IDEA на удивление еще хуже. И того получается, что список вроде бы большой, но выбрать особо не из чего(что было бы более ли менее привычным). В итоге я решил попробовать emacs в связке с clojure-mode. Должен признаться, что в некоторой степени на меня повлиял просмотр вот этого видео:
В статье я хочу рассказать о необходимых настройках, которые следует выполнить для того чтобы использовать emacs как Clojure-IDE. Все описанные шаги былы проделаны на Debian Wheezy в связке с emacs-snapshot.
Начальная конфигураця
Emacs из коробки не так хорошо, как мог бы быть, поэтому нам придется его немного допилить. В далекие времена, каждый уважающий себя девелопер почитал за честь создать свою уникальную конфигурацию(что-то вроде хобби, когда нечем заняться на досуге). Сейчас ситуация изменилась в лучшую сторону. Существует проект emacs-starter-kit(статья на хабре) - конфигурация, которую не стыдно взять за начальную.
В добавок ко всему, starter-kit поддерживается и развивается. Все что нам нужно - это сделать клон:
# perform this command from home directory
git clone https://github.com/technomancy/emacs-starter-kit.git .emacs.d
После добавить ~/home/.emacs.d/init.el, который у меня выглядит так:
(require 'package) (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/") t) (package-initialize) (setq inhibit-startup-message t) ;; dont show splash (global-linum-mode t) (column-number-mode 1) (global-hl-line-mode 1) (setq scroll-step 1) ;; line-by-line scrolling (setq make-backup-files nil) ;; prevent backup files (setq-default fill-column 120) (fset 'yes-or-no-p 'y-or-n-p) ;; use 'y' and 'n' instead 'yes' and 'no' (show-paren-mode 1) (electric-pair-mode 1) (if window-system (set-frame-size (selected-frame) 140 40)) (if window-system (tool-bar-mode -1)) ;; M-x tool-bar-mode (if window-system (scroll-bar-mode -1)) (if window-system (menu-bar-mode -1)) ;; M-x menu-bar-mode ;;(set-default-font "Inconsolata-11") ;;(set-default-font "Consolas-10") ;; ;; C-x C-g binding for go to line number ;; (global-set-key [?\C-x ?\C-g] 'goto-line) (require 'auto-complete) (global-auto-complete-mode t) (setq ac-auto-start nil) (setq ac-auto-start 3) ;; when entring 3 characters (define-key ac-complete-mode-map "\t" 'ac-complete) (define-key ac-complete-mode-map "\r" nil)
Установка пакетов
В самом начале следует обновить содержимое репозитария. Для этого в запущенном редакторе выполяем команду:
M-x package-refresh-contents [RET]
Инсталляции пакета - это последовательность двух команд:
M-x package-install [RET] package-name [RET]
Для работы с clojure нам понадобятся: starter-kit,
starter-kit-lisp,
starter-kit-bindings,
starter-kit-eshell,
clojure-mode,
clojure-test-mode,
nrepl.
Проверим рабочее окружение... Запускаем REPL:
M-x nrepl-jack-in
И пробуем выполнить произвольную операцию:
Clojure Test Mode
Я предпочитаю ТDD и, как мне кажется, все современные IDE должны предоставлять средства для данного стиля разработки. Помимо прочего clojure-mode содержит clojure-test-mode.el - модуль умеющий запускать тесты и представлять результаты.
Для запуска тестов текущего буфера используется сочетание клавиш C-c C-,, для очистки результатов- C-c k. Ошибки и непройденные тесты будут помечены маркерами:
Переключение между тестом и реализацией, осуществляется сочетанием клавиш C-c C-t. В корне проекта должна находиться директория src/, содержащая исходный код и директория test/ - юнит тесты. Пространство имен тестов должно быть таким же как и у исоходников с добавлением суффикса -test. Например, пространство имен my.project.frob имеет следующую организацию файлов src/my/project/frob.clj и тестов my.project.frob-test и test/my/project/frob_test.clj.
nrepl
nrepl - это клиент для Clojure networked REPL server, пришел на смену проекту SLIME + swank-clojure. Как упоминалось ранее, для запуска REPL служит команда M-x nrepl-jack-in(во время работы может быть запущено несколько сессий). Для закрытия текущей и для закрытия всех сессий - M-x nrepl-close, M-x nrepl-quit соответственно.
Команды редактируемого буфера(полный список можно посмотреть на оф. странице):
C-x C-e- показать результат в эхо-буфереC-c C-r- отобразить результат выделенной области в эхо буфереC-c C-b- прервать выполнение командыC-c M-n- переключить пространство имен текущего буфераC-c M-o- очистить REPL, оставить лишь приглашениеC-c C-k- загрузить буферC-c C-l- загрузить файлC-c C-d- показать документацию по текущему символуC-c C-s- показать сорцыC-c C-j- показать Javadoc(в броузере)M-.- перейти к декларации/определению символаM-,- вернуться на прежнее место(смь. предыдущий пункт)M-TAB- автодополнение/автозавершение символа
C-RET- дополнить все скобки выражение и вычислить результатC-j- перейти на новую строкуC-c M-o- очистить REPL, оставить лишь приглашениеC-c C-o- очистить результат предыдущего вычисленияC-c C-b/C-c C-с- прервать выполнение командыC-up/C-down- переход по истории командC-c C-d- показать документацию по текущему символу
Плюшки
Для работы с clojure-mode(и не только) рекомендуется использовать Paredit. Данный пакет в разы облегчает редактирование кода. Установить можно из репозитария. Для перманентной активации необходимо добавить следующую строчку в файл конфигурации:
;; (require 'paredit) if you didn't install it via package.el (add-hook 'clojure-mode-hook 'paredit-mode)
Краткий справочник по Paredit можно найти здесь.
Линки
- IDE и редакторы на dev.clojure.org/
- Emacs starter-kit
- Статья на хабре про emacs-starter-kit
- Оригинальный пост - настройка Emacs для работы с clojure
- Clojure-mode на github-e
- nREPL на github-е
- Справочник по Paraedit
Sunday, June 30, 2013
Задача о неподвижной точке функции
Число x называется неподвижной точкой (fixed point) функции f, если оно удовлетворяет равенству:
f(x)=x
В sicp предлагается следующий алгоритм решения данной задачи. Для некоторой функций f можно найти неподвижную точку, многократно вычисляя f(от предыдущего значения функции) до тех пор, пока изменение значения превышает условную пороговую величину(заданную точность):
f(x),f(f(x)),f(f(f (x))),...
Используя эту идею, напишем процедуру двух аргументов - функции и начального значения:
(defn fixed-point [f guess] (defn close-enough? [v1 v2] (< (abs (- v1 v2)) 0.00001)) (let [next (f guess)] (if (close-enough? guess next) next (recur f next))))
Для примера вычислим приближенное значение неподвижной точки функции косинуса:
(ns sicp.clojure.topic-1-3-4 (:use [clojure.contrib.generic.math-functions :only [cos]])) ... (fixed-point cos 1.0) ; -> 0.7390822985224024
Как можно заметить, данный способ имеет ряд недостатков. Если попытаться вычислить значение для функции kx+b, например, вот так:
=> (fixed-point (fn[x] (+ 0.5 (* 2 x))) 1)
То интерпретатор уходит в глубокое раздумье. Я подвожу к тому, что неплохо бы подумать об альтернативном способе. В прошлый раз мы решали задачу нахождения корней уравнения. Допустим у нас есть некая процедура half-interval-method трех аргументов - функция и границы интервала, умеющая находить корни уравнения вида f(x)=0. Тогда для нахождения неподвижной точки нужно решить:
f(x)-x=0
Выглядеть это будет так(код
(defn fixed-point [f x1 x2] (half-interval-method (fn[x] (- (f x) x)) x1 x2))
Ну и на последок:
(fixed-point cos 0.0 1.0) 0.7390899658203125 ... (fixed-point (fn[x] (+ 0.5 (* 2 x))) -3 3) -0.5000152587890625
Thursday, May 30, 2013
Нахождение корней уравнений методом половинного деления
Развлечения ради, временами появляется желание покрутить задачки, которые выходят за рамки повседневного программирования. В разделе SICP 1.3.3 есть интересная, но несложная задачка. Более того, решение там приведено, но прежде чем смотреть его я попытался решить задачу самостоятельно и потом сравнить результаты.
Метод половинного деления (half-interval method) — это простой, но мощный способ нахождения корней уравнения f(x)=0, где f — непрерывная функция. Идея состоит в том, что если нам даны такие точки a и b, что f(a)<0<f(b), то функция f должна иметь по крайней мере один ноль на отрезке между a и b. Чтобы найти его, возьмем x, равное среднему между a и b, и вычислим f(x). Если f(x)>0, то f должна иметь ноль на отрезке между a и x. Если f(x)<0, то f должна иметь ноль на отрезке между x и b. Продолжая таким образом, мы сможем находить все более узкие интервалы, на которых f должна иметь ноль. Когда мы дойдем до точки, где этот интервал достаточно мал, процесс останавливается. Поскольку интервал неопределенности уменьшается вдвое на каждом шаге процесса, число требуемых шагов растет как Θ(log(L/T)), где L есть длина исходного интервала, а T есть допуск ошибки (то есть размер интервала, который мы считаем «достаточно малым»).
Касательно реализации. Она достаточно упрощенная(напр. я не осуществляю проверку того, что a меньше b):
(ns sicp.clojure (:use [clojure.test :only [is]]) (:use [clojure.contrib.generic.math-functions :only [sin]]) (:use [clojure.contrib.math :only [abs]])) (defn get-middle [left right] (/ (+ left right) 2)) (defn get-adge-point [f middle x] (let [y1 (f middle) y2 (f x)] (cond (and (> y1 0) (> y2 0))middle (and (< y1 0) (< y2 0))middle :else x))) (defn good-enough? [left right] (< (- right left) 0.0001)) (defn half-interval-method-iter [f left middle right] (if (good-enough? left right) (get-middle left right) (let [left (get-adge-point f middle left) middle (get-middle left right) right (get-adge-point f middle right)] (recur f left middle right)))) (defn half-interval-method [f a b] (double (half-interval-method-iter f a (get-middle a b) b))) ;; sin(x)=0 (is (= 3.141571044921875 (half-interval-method sin 2 4))) ;; x^3 − 2x − 3 = 0 (is (= 1.893280029296875 (half-interval-method (fn [x] (- (* x x x) (* 2 x) 3)) 1 2)))
Для получения очередного(половинного) интервала следуем правилу:
f(x) не меняет свой знак по отношению к f(a), новый интервал -> [x:b] f(x) не меняет свой знак по отношению к f(b), новый интервал -> [a:x]
Функция get-adge-point реализует этот алгоритм. x - среднее значение для a и b, которое вычисляется при помощи get-middle. good-enough? - вспомогательная функция для проверки достигли мы заданной точности при вычислении результата. Для сравнения реализация на java:
public class HalfMidleMethod { public static interface Fn { double f(double x); } private boolean isGoodEnough(double left, double right) { return (right - left) < 0.0001; } private double getAdgePoint(Fn y, double middle, double x) { double y1 = y.f(middle); double y2 = y.f(x); if (y1 > 0 && y2 > 0) { return middle; } else if (y1 < 0 && y2 < 0) { return middle; } else { return x; } } private double getMiddle(double left, double right) { return (left + right) / 2; } private double getRoot(Fn y, double left, double middle, double right) { //System.out.println("left=" + left + ", middle=" + middle + ", right=" + right); if (isGoodEnough(left, right)) { return getMiddle(left, right); } left = getAdgePoint(y, middle, left); right = getAdgePoint(y, middle, right); middle = getMiddle(left, right); return getRoot(y, left, middle, right); } public double getRoot(Fn y, double left, double right) { return getRoot(y, left, getMiddle(left, right), right); } public static void main(String[] arvg) { Fn sin = new Fn() { @Override public double f(double x) { return Math.sin(x); } }; System.out.println("Pi=" + new HalfMidleMethod().getRoot(sin, 4, 7)); } }
Т.к. java целиком и полностью объектно ориентированный язык, то как следствие в нем нет функций высших порядков. Для передачи функции в качестве аргумента используется интерфейс HalfMidleMethod.Fn.

