Showing posts with label clojure. Show all posts
Showing posts with label clojure. Show all posts

Wednesday, April 1, 2015

Clojure Tips

Not so far, I found interesting resource, which contains tips for different programming languages. Here is the one for the clojure:

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 - автодополнение/автозавершение символа
Команды REPL буфера:
  • 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 можно найти здесь.

Линки

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

Выглядеть это будет так(код half-interval-method я приводить не буду, его можно посмотреть в предыдущем посте):

(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.