Startseite bisherige Projekte Tools/Snippets Bücherempfehlungen Publikationen Impressum Datenschutzerklärung

Clojure SnippetsDezember 2020

Beispiele aus dem Buch Programming Clojure von Miller, Halloway, Bedra und von der Seite Brave Clojure.
    (ns test.core)
    ; Comments with ;
    (comment "Comments out blocks")
    1000M ; BigDecimal
    1000N ; BigInt
    
    
    [1 2 3] ; Vector
    '(1 2 3) ; List
    #{1 2 3} ; Set
    {:a 1 :b 2} ; Map
    
    
    (str 1 2 3) ; 123 - Convert to String and concatenate
    (true? true) ; true
    (false? true) ; false
    (nil? nil) ; true
    (zero? 1) ; false
    
    
    (string? "Hello") ; true
    (keyword? :hello) ; true
    (symbol? 'hello) ; true
    
    (defn myfunc
      "description"
      [arg1 arg2]
      (str arg1 arg2))
    
    (fn [arg1 arg2] (str arg1 arg2)) ; anonymous function
    #(str %1 %2) ; anonymous function
    ; also see letfn-macro below
    
    (def foo 10) ; create var
    (var foo) ; returns var
    #'foo ; returns var
    
    
    (let [x "1"
          y "2"]
      (str x y)) ; create lexical bindings
    
    
    ; destructuring
    (defn greet-2
      [{fname :first-name}]
      (println "Hello," fname))
    (greet-2 {:first-name "Hans" :last-name "Test"})
    
    ; more destructuring
    (let [[x y] [1 2 3]] [x y]) ; [1 2]
    (let [[_ _ z] [1 2 3]] z) ; 3
    (let [[x y :as coords] [1 2 3 4 5 6]]
      (str x y " total: " (count coords))) ; binds coords to collection: "12 total: 6
    
    
    (in-ns 'myapp) ; switch to namespace myapp
    (clojure.core/use 'clojure.core) ; use clojure core namespace
    (ns examples.coloring ; set namespace to examples.coloring, macro  supports :require, :import, :use
      (:require [clojure.string :as str])
      (:import (java.io.file)))
    
    
    ; Java access
    ; (.method instance & args)
    ; (.field instance)
    ; (.-field instance)
    ; (Class/method & args)
    ; (Class/field)
    (java.util.Random.) ; new instance
    (System/lineSeparator) ; static field
    Math/PI ; static field
    (java.util.Random.) ; new instance
    (.nextInt (java.util.Random.) 10) ; call Method
    
    
    ; flow control
    (if (< 3 4) ; if-clause
      true
      false)
    
    ; cond
    (defn condexample [x]
      (cond
        (= x 5) (println "x is 5")
        (= x 10) (println "x is 10")
        :else (println "x is not defined")))
    
    (do (println "do multiple things") ; do multiple things, return value of last expression
        "help")
    
    (loop [result [] ; loop sets recursion point, use recur to re-enter
           x 5]
      (if (zero? x)
        result
        (recur (conj result x) (dec x))))
    
    (defn countdown [result x] ; recur works also with functions
      (if (zero? x)
        result
        (recur (conj result x) (dec x))))
    (countdown [] 5) ; [5 4 3 2 1]
    
    
    ;working with sequences
    
    (first [1 2 3]) ; 1
    (rest [1 2 3]) ; (2 3)
    (cons 0 [1 2 3]) ; (0 1 2 3) "cons" for "construct"
    
    (seq "Hallo") ; return sequence oof seq-able collection, e.g. (\H \a \l \l \o)
    (seq? [1 2 3]) ; false
    (seq? (rest [1 2 3])) ; true
    (sorted-set 5 2 1 3 4) ; #{1 2 3 4 5}
    (sorted-map :c 3 :b 1 :a 2) ; {:a 2, :b 1, :c 3}
    
    (conj '(1 2 3) :a) ; (:a 1 2 3) ; for lists, add at front
    (into '(1 2 3) '(:a :b :c)) ; (:c :b :a 1 2 3); for lists, add at front
    
    (conj [1 2 3] :a) ; [1 2 3 :a] for vectors, add at end
    (into [1 2 3] [:a :b :c]) ; [1 2 3 :a :b :c] for vectors, add at end
    
    ; Using the sequence library
    (range 5) ; (0 1 2 3 4) end only
    (range 5 10) ; (5 6 7 8 9) start + end
    (range 1 10 2) ; (1 3 5 7 9) start, end, step
    (repeat 5 1) ; (1 1 1 1 1)
    (take 10 (iterate inc 1)) ; (1 2 3 4 5 6 7 8 9 10) iterate begins with value and continues forever, applying a function to each value to calculate the next
    (take 5 (repeat 2)) ; (2 2 2 2 2) with one argument repeat returns infinite sequence
    (take 6 (cycle (range 3))) ; (0 1 2 0 1 2)
    
    (def whole-numbers (iterate inc 1))
    (interleave whole-numbers ["A" "B" "C" "D"]) ; (1 "A" 2 "B" 3 "C" 4 "D") stops when one of the collections is exhausted
    (apply str (interpose ", " ["apples" "bananas" "grapes"])) ; apples, bananas, grapes
    ; shortcut for (apply str (interpose ...))
    (require '[clojure.string :refer [join]])
    (join ", " ["apples" "bananas" "grapes"]) ; apples, bananas, grapes
    
    ; create collections
    (list 1 2 3 4) ; (1 2 3 4)
    (vector 1 2 3 4) ; [1 2 3 4]
    (hash-set 1 2 3 4) ; #{1 4 3 2}
    (hash-map :a 1 :b 2 :c 3) ; {:c 3, :b 2, :a 1}
    (set [1 2 3 4]) ; #{1 4 3 2}
    (vec (range 3)) ; [0 1 2]
    
    ; filtering
    (take 10 (filter even? whole-numbers)) ; (2 4 6 8 10 12 14 16 18 20)
    
    (def vowel? #{\a \e \i \o \u}) ; Sets act as functions that return if a value is in the set
    (def consonant? (complement vowel?)) ; complement reverses the value of another function
    (take-while consonant? "the-q") ; (\t \h)
    (drop-while consonant? "the-q") ; (\e \- \q)
    (split-at 5 (range 10)) ; [(0 1 2 3 4) (5 6 7 8 9)]
    (split-with #(<= % 10) (range 0 20 2)) ; [(0 2 4 6 8 10) (12 14 16 18)]
    
    ; seq predicates
    (every? odd? [1 3 5]) ; true
    (some odd? [2 3 6]) ; true - returns function value of first non-null
    (some #{3} (range 10)) ; 3
    (not-every? even? whole-numbers) ; true
    (not-any? even? whole-numbers) ; false
    
    ; transforming
    (map #(format "<p>%s</p>" %) ["the" "quick" "brown"]) ; ("<p>the</p>""<p>quick</p>" "<p>brown</p>")
    (reduce + (range 1 11)) ; 55 function must have two arguments. Applies to first two elements in coll, then the result with the third, ...
    (sort [47 11 50]) ; (11 47 50)
    (sort > [47 11 50]) ; (50 47 11), uses function for sorting
    (sort-by #(.toString %) [47 11 50]) ; (11 47 50)
    
    ; list comprehension
    (for [word ["the" "quick" "brown " "fox"]] ((co)mat "<p>%s</p>" word)) ;("<p>the</p>" "<p>quick</p>" "<p>brown </p>" "<p>fox</p>")
    (take 10 (for [n whole-numbers :when (even? n)] n)) ; (2 4 6 8 10 12 14 16 18 20), filter with (:when)
    (for [n whole-numbers :while (odd? n)] n) ; (1) :while continues only while expression is true
    (for [file "ABC" rank (range 1 4)] (format "%c%d" file rank)) ; ("A1" "A2" "A3" "B1" "B2" "B3" "C1" "C2" "C3") rightmost first, then to the left
    
    ; make sure lazy seqs are evaluated
    (doall [1 2 3]) ; [1 2 3]
    (dorun [1 2 3]) ; nil - will not keep results in memory, good for very large colls
    
    ; Java Seq-able
    (first (.getBytes "hello")) ; 104
    (rest (.getBytes "hello")) ; (101 108 108 111)
    (first (System/getProperties)) ; #<Entry java.runtime.name=Java(TM) SE Runtime Environment>
    (first "Hello") ; \H
    (apply str (reverse "Hello")) ; olleH
    (re-seq #"\w+" "the quick brown fox") ; ("the" "quick" "brown" "fox") re-seq exposes an immutable seq over the matches
    (seq (.listFiles (java.io.File. "."))) ; sequence of files
    (count (file-seq (java.io.File. "."))) ; 92 - depth-first walk via file-seq
    
    (require '[clojure.java.io :refer [reader]])
    (take 2 (line-seq (reader "README.md"))) ; ("# test" "") note: does not close reader
    (with-open [rdr (reader "README.md")] (count (line-seq rdr))) ; use with-open to close the reader
    
    ; Functions on lists
    (peek '(1 2 3)) ; 1 first element
    (pop '(1 2 3)) ; (2 3)
    (rest '()) ; ()
    (pop '()) ; IllegalStateException Can't pop empty list
    
    ; Functions on vectors
    (peek [1 2 3]) ; 3 - last element
    (pop [1 2 3]) ; [1 2] - removes last element
    (get [\a \b \c] 2) ; \c, nil if out of range
    ([\a \b \c] 1) ; \b - vectors are functions, they return the value at the index
    ([\a \b \c] 3) ; does not work: IndexOutOfBoundsException clojure.lang.PersistentVector.arrayFor (PersistentVector.java:107)
    (assoc [0 1 2 3 4] 2 :two) ; [0 1 :two 3 4] - associate a new value with an index
    (subvec [0 1 2 3 4] 2) ; [2 3 4] - start
    (subvec [0 1 2 3 4] 2 3) ; [2] - start and end
    (concat ["a" "b" "c"] ["d" "e"]);("a" "b" "c" "d" "e") NOT A VECTOR
    
    ; Functions on maps
    (keys {:name "Hans" :lastname "Test" :age 32}) ; (:age :lastname :name)
    (vals {:name "Hans" :lastname "Test" :age 32}) ; (32 "Test" "Hans")
    (get {:name "Hans" :lastname "Test" :age 32} :name) ; Hans - get value or null
    (get {:name "Hans" :lastname "Test" :age 32} :street "unknown") ;unknown, default value
    ({:name "Hans" :lastname "Test" :age 32} :age) ; 32 - maps are functions of their keys
    (:name {:name "Hans" :lastname "Test" :age 32}) ; Hans - keywords are also functions
    (contains? {:name "Hans" :lastname "Test" :age 32 :val nil} :val) ; true, map contains val with value null
    
    
    (assoc {:name "Hans"} :key "value") ; {:key "value", :name "Hans"}
    (dissoc {:key "value", :name "Hans"} :name) ; {:key "value"}
    (select-keys {:name "Hans" :lastname "Test" :age 32} '(:name :age)) ;{:age 32, :name "Hans"}
    (merge {:name "Hans" :age 32} {:name "Jens" :lastname "Test"}) ; {:lastname "Test", :age 32, :name "Jens"} - if multiple maps contain a key, the rightmost wins
    (merge-with concat {:name "Hans" :age 32} {:name "Jens" :lastname "Test"}) ; {:lastname "Test", :age 32, :name (\H \a \n \s \J \e \n \s)} - specify a function for merging
    
    ; Functions on sets
    (require '[clojure.set :refer :all]) ; more functions for working with set
    (def languages #{"java" "c" "d" "clojure"})
    (def beverages #{"java" "chai" "pop"})
    (union languages beverages) ;{ "d" "clojure" "pop" "java" "chai" "c"}
    (intersection languages beverages) ; #{"java"}
    (difference languages beverages) ; elements of first set minus elements of second set
    (select #(.startsWith % "j") languages) ; #{"java"} - select by predicate
    
    ;Regular expressions
    (re-seq #"\w+" "the quick brown fox") ; ("the" "quick" "brown" "fox") re-seq exposes an immutable seq over the matches
    (re-find #"^left-" "left-eye"); #"" is regexp. If groups are used, returns vector
    (re-matches #"\w+" "hallo");return match or nil
    (re-pattern (str "a" "b")); creates a pattern for regexp
    
    ; work with sets and maps as database (similar to SQL)
    (def testdb #{{:nr 1 :name "Jens" :lastname "Tester"} {:nr 2 :name "Karl" :lastname "Schmidt"} {:nr 3 :name "Gunther" :lastname "Eck"}})
    (rename testdb {:nr :index}) ; {{:index 2, :lastname "Schmidt", :name "Karl"} {:index 1, :lastname "Tester", :name "Jens"} {:index 3,:lastname "Eck", :name "Gunther"}}
    (select #(= (:lastname %) "Schmidt") testdb) ; #{{:lastname "Schmidt", :name "Karl", :nr 2}} - select by predicate
    (project testdb [:name]) ; #{{:name "Karl"} {:name "Gunther"} {:name "Jens"}}
    ; (join relation-1 relation-2 keymap?)
    
    ; Fibonacci that consumes the stack - not a good idea
    (defn stack-consuming-fibo [n]
      (cond
        (= n 0) 0
        (= n 1) 1
        :else (+ (stack-consuming-fibo (- n 1))
                 (stack-consuming-fibo (- n 2)))))
    
    ; Better use tail recursion
    (defn tail-fibo [n]
      (letfn [(fib
                [current next n]
                (if (zero? n)
                  current
                  (recur next (+ current next) (dec n))))]
        (fib 0N 1N n)))
    
    ; even better use lazy seq
    (defn lazy-seq-fibo
      ([]
       (concat [0 1] (lazy-seq-fibo 0N 1N)))
      ([a b]
       (let [n (+ a b)]
         (lazy-seq (cons n (lazy-seq-fibo b n))))))
    
    (take 10 (lazy-seq-fibo)) ; (0 1 1N 2N 3N 5N 8N 13N 21N 34N)
    (rem (nth (lazy-seq-fibo) 1000000) 1000) ; 875N - last 3 digits of millionth fib-number
    
    ; simpler: Use iterate
    (take 5 (iterate (fn [[a b]] [b (+ a b)]) [0 1])) ; ([0 1] [1 1] [1 2] [2 3] [3 5])
    
    (defn fibo []
      (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))) ; (0 1 1 2 3 5 8 13 21 34)
    
    ; comp and partial application
    (def count-if (comp count filter)) ; composed function. Applies rightmost function to argumentens, then next-rightmost to result, ...
    (count-if odd? [1 2 3 4 5 6 7]) ; 4
    
    (def add-3 (partial + 3)) ; partial application of function
    (add-3 2) ; 5
    
    ; mutual recursion
    (declare my-odd? my-even?) ; might require declare
    
    (defn my-odd? [n]
      (if (= n 0)
        false
        (my-even? (dec n))))
    
    (defn my-even? [n]
      (if (= n 0)
        true
        (my-odd? (dec n))))
    
    (my-odd? 3) ; will not work well for large numbers
    
    ; try self-recursion instead
    (defn parity [n]
      (loop [n n par 0]
        (if (= n 0)
          par
          (recur (dec n) (- 1 par)))))
    
    (defn my-even2? [n]
      (= 0 (parity n)))
    
    (defn my-odd2? [n]
      (= 1 (parity n)))
    
    (my-odd2? 5) ; true
    
    ; trampoline
    ; if return is not a function, trampoline works like calling the function directly
    (trampoline list) ; ()
    (trampoline + 1 2) ; 3
    
    ; if return is a function, then trampoline assumes you want to call it recursively and calls it for you
    ; trampoline manages its own recur, so it will keep calling your function until it stops returning functions.
    (defn trampoline-fib [n]
      (let [fib (fn fib [f-2 f-1 current]
                  (let [f (+ f-2 f-1)]
                    (if (= n current)
                      f
                      #(fib f-1 f (inc current)))))] ; return anonymous function
        (cond
          (= n 0) 0
          (= n 1) 1
          :else (fib 0N 1 2))))
    
    (trampoline trampoline-fib 9) ; 34N
    
    ; Memoization
    (declare m f)
    (defn m [n]
      (if (zero? n)
        0
        (- n (f (m (dec n))))))
    (defn f [n]
      (if (zero? n)
        1
        (- n (m (f (dec n))))))
    
    (time (m 100)) ; prints "Elapsed time: 100.2971 msecs", takes quite long
    
    ; rebind to memoized functions
    (def m (memoize m))
    (def f (memoize f))
    
    (time (m 100)) ; will only need 0.0241 msecs
    
    ; however, large values will still blow the stack when starting with empty cache
    (def m-seq (map m (iterate inc 0)))
    (def f-seq (map f (iterate inc 0)))
    
    (take 10 m-seq); (0 0 1 2 2 3 4 4 5 6)
    
    (time (nth m-seq 10000)); 6180, prints "Elapsed time: 30.7671 msecs", quite fast!
    
    (doseq [x (seq)] (println x)) ; apply to every member of sequence, return nil 
    
Impressum - Datenschutzerklärung