Clojure Basics

Основы

Везде, где можно поставить пробел, можно поставить и запятую

(println "Hello, Clojure!")

(println (+ 1 2 3 4))
(println (type 1))
(println (type 1/2))
(println (type 1.45))

(println (not "hello"))
(println (not nil))
(println (not false))

Данный код выведет:

Hello, aboba228!
10
java.lang.Long
clojure.lang.Ratio
java.lang.Double
false
true
true

Тут есть аналог больших чисел:

(println (+ 10N 20N))                                       ; Big Int тип

Функции и переменные

(def add +)                                               ; умеем задавать
(println (add 10 20 30))


; def - специальная форма
(def x (* 30 40))                                           ; переменная
(println x)

(defn square [x] (* x x))                                   ; функция
(println (square x))

(println ((fn [x] (+ x x)) 10))                             ; функции могут быть анонимными

(defn twice [f] (fn [x] (f (f x))))                         ; функции могут быть от функций
(println ((twice square) 10))
(println (#(+ %1 %2) 10 30))

Рекурсия

(defn rec-fib [n]
  (cond (== 0 n) 1
        (== 1 n) 1
        :else (+ (rec-fib (- n 1)) (rec-fib (- n 2)))       ; сюда вместо else можно написать арбуз
        )
  )
(println (mapv rec-fib (range 1 10)))

Как и ожидается данная функция будет работать, как числа фиббоначи.

(def rec-fib2
  (fn [n] (cond (== 0 n) 1
                (== 1 n) 1
                :else (+ (rec-fib2 (- n 1)) (rec-fib2 (- n 2)))
                ))
  )
(println (mapv rec-fib2 (range 1 10)))

Улучшим и теперь наша функция будет сохранять результат вычислений

(def rec-fib3
  (memoize (fn [n] (cond (== 0 n) 1N
                         (== 1 n) 1N
                         :else (+ (rec-fib3 (- n 1)) (rec-fib3 (- n 2)))
                         ))
           ))
(println (mapv rec-fib3 (range 1 10)))
(println (rec-fib3 100))

memoize внутри себя честно кеширует. Но у нас стек заканчивается и тильт

Всякие приколы

(defn iter-fib' [n a b]
  (if (== 0 n)
    a
    (iter-fib' (- n 1) b (+' a b)))
  )

(defn iter-fib [n]
  (iter-fib' n 1 1))

(println (mapv iter-fib (range 1, 10)))

Мы можем это заменить

(defn iter-fib [n]
  (letfn [(rec [n a b]
     (if (== 0 n)
       a
       (recur (- n 1) b (+' a b))))]
  (rec n 1 1)))

(println (mapv iter-fib (range 1, 10)))

Синтаксический сахар

(defn iter-fib [n']
  (loop [n n' a 1 b 1]
     (if (== 0 n)
       a
       (recur (- n 1) b (+' a b)))))

(println (mapv iter-fib (range 1, 10)))

Pred Post

(defn power
  "Raises a to b-th power"
  [a,b]
  {:pre[(<= 0 b)]
   :post[(or (== b 0) (zero? a) (zero? (mod % a)))]}
  (cond
    (zero? b) 1
    (even? b) (power (* a a) (quot b 2))
    (odd? b) (* a (power a (dec b)))
    )
  )
(println (power 2 10))

Стандартная библиотека

(list 10 20 30)
(list? (list 10 20 30)) ; предикат для списка
(count (list 10 20 30)) ; 3

Это список. Бывает пустой. Круглые скобки - пустой

Листы реализованы связными списками.

[10 20 30] ; вектор
(count [10 20 30]) ; 3

Есть пару веселых функций:

(conj [10 20 30] 100 200) ; => [10 20 30 100 200]

Вектор ведет себя как стек, лист как стек сначала.

(mapv) честно вернет вектор от того, что вы просили
(identity 30) ; => 30
((constatly 30) 34892312 213512 3120913) => 30
((comp square inc) 10) ; обычная композиция