Clojure — это диалект LISP ; Если вы когда-либо были вынуждены использовать LISP на уроках информатики, я понимаю ваше сопротивление этому классу полностью функциональных языков.
Фактически, LISP для многих из нас означает много обратной польской нотации, где (+ 1 2) оценивается как три; отсутствие циклов и других товаров для привилегированной хвостовой рекурсии; и абсолютное отсутствие состояния в виде переменных (состояние на самом деле отсутствует: я видел его в стеке).
Однако Clojure является языком общего назначения и привлек внимание великих умов, таких как дядя Боб, который доходит до того, что предлагает стать нашим единственным стандартным языком.
Известный факт о Clojure заключается в том, что он может компилироваться в байт-код JVM или CLR , что означает, что он может быть развернут на тех же платформах, что и приложения Java или C #. Это определенно нужно учитывать, и, как и для Scala, это положительно влияет на принятие языка.
Начиная?
Загрузите и разархивируйте
последний выпуск Clojure . Запустите класс, указав clojure-1.3.0.jar в classpath:
java -cp clojure-1.3.0.jar clojure.main
(проверьте версию: на момент написания этой статьи последний стабильный выпуск был 1.3.0.) Вы можете играть с циклом Read-Eval-Print-Loop, как вы могли бы делать с Python или Ruby:
[15:45:20][giorgio@Desmond:~/clojure-1.3.0]$ java -cp clojure-1.3.0.jar clojure.main Clojure 1.3.0 user=> (+ 2 3) 5 user=>
Однако добавьте это в ваш .bashrc для удобства:
alias clj="java -jar ~/clojure-1.3.0/clojure-1.3.0.jar"
Теперь вы можете набрать
clj file.clj для выполнения кода Clojure.
Введение в язык?
Я уже немного знал LISP, поэтому пропустил введение. Существует длинный учебник, который поможет вам полностью перейти от императивного стиля, если вы еще не сталкивались с диалектом LISP, таким как Scheme или Common LISP. Есть также несколько примеров , посвященных стилю Scala и Ruby Koan, но без комментариев дзен. Они запускаются в браузере, поэтому могут показаться неестественными.
Таким образом, как можно исследовать Clojure, пропуская скучные введения? С простым ката, самостоятельным программированием : я предпочитаю заниматься вместо чтения. Ката FizzBuzz — это мой стандартный способ определения, является ли человек разработчиком, и изучение нового языка или среды . Это похоже на изменение одного параметра эксперимента, такого как температура, вес или концентрация, чтобы ясно понять, что происходит, не путая переменные в пути.
Однако для выполнения ката мне нужен способ проверить мой код. К счастью, с Clojure поставляется среда тестирования .
тестирование
Тесты могут быть определены как выражения, которые оцениваются как true, и это все, что вам нужно в функциональном языке.
(ns fizzbuzztests
(:use clojure.test))
(deftest test-adder
(is (= 24 (+ 2 22))))
(deftest test-adder
(is (= 24 (+ 2 23))))
(run-tests 'fizzbuzztests)
Выполнение этого кода покажет неудачный тест, конечно:
Testing fizzbuzztests FAIL in (test-adder) (fizzbuzztest.clojure:6) expected: (= 24 (+ 2 23)) actual: (not (= 24 25)) Ran 1 tests containing 1 assertions. 1 failures, 0 errors.
FizzBuzz ката
Я выполнил ката, добавив один тест за один раз , и много гуглял между провальным тестом (легко написать) и его реализацией и последующим рефакторингом. Мне пришлось искать, как определить функцию, где был оператор целочисленного модуля, выражение let для рефакторинга, конкатенация строк … Но я думаю, что я изучил довольно много базовых функций.
Целью, которую я поставил, было написание функции (fizzbuzz x) . Конечно, код, который я получаю, ужасен и полон ifs (они тоже плохи в функциональном программировании, верно?) Но он работает, он выразителен и является отправной точкой для изучения больше о Clojure.
Осторожно, спойлеры
Вот окончательный результат по сравнению с императивным языком. Имейте в виду, что я всегда использовал императивный язык, такой как Java, PHP или JavaScript, поэтому на этот ката может повлиять мой предыдущий опыт (и я только что обнаружил сокращение defn ).
Кстати, если вы думаете, что JavaScript сильно отличается от Java, то сейчас самое время попробовать Clojure или другие LISP. Вот версия JavaScript:
function FizzBuzz(correspondences) {
this.correspondences = correspondences;
this.accept = function (number) {
var result = '';
for (var divisor in this.correspondences) {
if (number % divisor == 0) {
result = result + this.correspondences[divisor];
}
}
if (result) {
return result;
} else {
return number;
}
}
}
А вот Clojure , в комплекте с тестами:
(ns fizzbuzz
(:use clojure.test))
(def divides (fn [x divisor]
(= 0 (mod x divisor))))
(def nameNumbers (fn [x mappingsOfNumbers]
(if (empty? mappingsOfNumbers)
""
(let [divisor (key (first mappingsOfNumbers))
value (val (first mappingsOfNumbers))]
(if (divides x divisor)
(str value (nameNumbers x (rest mappingsOfNumbers)))
(nameNumbers x (rest mappingsOfNumbers)))))))
(def fizzbuzz (fn [x]
(let [names (nameNumbers x {3 "Fizz" 5 "Buzz"})]
(if (= "" names)
x
names))))
(deftest test1ShouldBeLeftUntouched
(is (= 1 (fizzbuzz 1))))
(deftest test2ShouldBeLeftUntouched
(is (= 2 (fizzbuzz 2))))
(deftest test3isFizz
(is (= "Fizz" (fizzbuzz 3))))
(deftest test5isBuzz
(is (= "Buzz" (fizzbuzz 5))))
(deftest test6isFizz
(is (= "Fizz" (fizzbuzz 6))))
(deftest test10isBuzz
(is (= "Buzz" (fizzbuzz 10))))
(deftest test15isFizzBuzz
(is (= "FizzBuzz" (fizzbuzz 15))))
(run-tests)