Clojure: первое знакомство

Уж не знаю, по какой причине мне вдруг захотелось познакомиться с Clojure. До этого я практически не работал с функциональными языками (несколько маленких программ на Scala не учитываются). Однако рано или поздно приходится переступить через это. Почему я не выбрал Haskell или Erlang? Потому что Clojure крепко дружит с JVM, а это значит, что код на Java доступен в Clojure и наоборот. С другой стороны, почему не Scala? Scala не является абсолютно-функциональным языком, а это значит, что имея в руках функциональный и объектно-ориентированый язык программирования, рано или поздно в силу сложившихся привычек перейдешь на чистое ООП, используя его даже там, где язык предлагает более изящное решение на “функциональщине”.

Hello World

Начну с самого простого и традиционного - “Hello, World!”. Так как Clojure имеет некоторый Lisp-синтаксис, нам придется свыкнуться с мыслью о том, что нас ожидает великое множество скобочек:

Sample code
1
2
3
user=> (println "Hello, World")
Hello, World
nil

Что это за nil? Как я понял, листая примеры, это значит, что функция println ничего не вернула, а ничего - это nil или null в Java.
В целом, синтаксис вызова функции такой: (func arg1 arg2 .. argN). Так, выше func это println, а единственный аргумент представлен в виде строки "Hello, World"

Арифметика

После этого я решил глянуть, как обстоят дела с примитивной арифметикой - сложение, вычитание, деление, умножение. И тут меня ждал сюрприз:

Sample code
1
2
3
4
5
6
7
8
user=> (+ 5 6)
11
user=> (* 5 6)
30
user=> (- 5 6)
-1
user=> (/ 5 6)
5/6

Как видим, деление вернуло результат в виде рациональной дроби. А что, если попробовать так?

Sample code
1
2
3
4
5
6
user=> (/ 6 5)
6/5
user=> (/ 6 3)
2
user=> (/ 6 2)
3

Если возможно провести целочисленное деление, язык делает это совершенно самостоятельно.
А как получить остаток от деления? В С или Java это %, но тут это уже не работает. Мысленно перебирая варианты, пробую такие названия функций: mod rem. И каково мое удивление, эти оба варианта работают и выполняют одно и то же действие. Сказать, зачем это сделано, я не могу, потому просто будем использовать то, что первое придет в голову:

Sample code
1
2
3
4
5
6
7
8
9
10
user=> (rem 6 2)
0
user=> (rem 6 4)
2
user=> (mod 6 1)
0
user=> (mod 6 10)
6
user=> (rem 6 10)
6

Строки

Строки в языке записываются также, как и в большинстве других языков - между двойных кавычек:

Sample code
1
2
3
4
5
6
7
8
user=> (str "Hello, " "World")
"Hello, World"
user=> "Hello"
"Hello"
user=> (str "Hello" 9)
"Hello9"
user=> (str "Hello" [5 6 7])
"Hello[5 6 7]"

С поиском в строке вышло немного труднее - не имея представления о том, как нужно делать вызов функций из Java, я пытался получить indexOf из java.lang.String. Но, полазив по официальному сайту, нашел:

Sample code
1
2
3
4
5
6
user=> (.indexOf "Hello" "l")
2
user=> (.lastIndexOf "Hello" "l")
3
user=> (.substring "Hello" 1 3)
"el"

Другие типы данных

Давайте еще глянем на такие вещи, как списки, множества, вектора и карты:

Sample code
1
2
3
4
5
6
7
8
user=> [1 2 3 4 5]
[1 2 3 4 5]
user=> {:1 2 :3 4}
{:1 2, :3 4}
user=> (set '(1 2 3 3 4 4 4 5 5 5 5 5))
#{1 4 3 2 5}
user=> '(\a \b \c \d)
(\a \b \c \d)

Язык предлагает поддержку различных типов этих структур - сортированные, хеш-, неупорядоченные.

Заключение

Для первого достаточно, позже я рассмотрю управляющие операторы, функции, переменные. Конечно, можно писать и писать, однако мне кажется, что уже размер статьи стал несколько большим, потому продолжу знакомство в следующей статье.

Ссылки