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)