Mercurial > repos > other > SevenLanguagesInSevenWeeks
annotate 6-Clojure/README.txt @ 79:29025305bbb7
Add notes and exercises from Day 1 of Clojure
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 01 Jun 2019 20:10:59 +0100 |
parents | |
children | a83309cdf5d3 |
rev | line source |
---|---|
79
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
1 Install Clojure with "curl -O https://download.clojure.org/install/linux-install-1.10.0.442.sh; chmod +x linux-install-1.10.0.442.sh; sudo ./linux-install-1.10.0.442.sh" (it's a downloader that downloads the real package) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
2 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
3 Clojure has a repl (clj), which needs rlwrap ("sudo zypper in rlwrap"). This then downloads some MORE files from Maven. Don't you just LOVE the Java ecosystem?! |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
4 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
5 Clojure is a Lisp dialect. It feels a little like Prolog in that it's logic and brackets galore, but Prolog is about predicates and pattern matching whereas Lisp has more of an imperative language with odd syntax |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
6 but sensible things like "if" statements. It uses strong, dynamic typing. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
7 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
8 Running commands at the repl and prints output *and* the return value (which can be "nil"). |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
9 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
10 Commands are prefix, not infix: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
11 "Normal" languages: 1 + 1 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
12 Clojure: (+ 1 1) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
13 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
14 Division of integers creates a (normalised) "ratio" type to maintain accuracy: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
15 (/ 2 4) → 1/2 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
16 (/ 2.0 4) → 0.5 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
17 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
18 Prefix does have its advantages, though - it's more like a function and can have variable numbers of parameters: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
19 (+ 2 2 2 2) → 8 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
20 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
21 And it can be used to check order: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
22 (< 1 2 3) → true |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
23 (< 1 3 2 4) → false |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
24 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
25 Strings are double-quoted, but characters can be escaped with a backslash outside quotes to become a string. When done with "str" it makes a string: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
26 (str \h \e \l \l \o) → "hello" |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
27 This seems like it would rarely be useful. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
28 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
29 Strings and characters are different (just like in Java) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
30 (= "a" \a) → false |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
31 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
32 Clojure can pass code as arguments, e.g.: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
33 (if true (println "It's true!")) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
34 but that's quite normal for functional languages. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
35 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
36 The "if" function is basically "if <condition> <true>[ <false>]". |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
37 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
38 Note: 0 and "" are true but nil is false, so only false and the null value are falsey. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
39 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
40 Clojure has four main data structures - lists (for code), vectors (for data), sets and maps. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
41 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
42 You can't build a list Python style with "(1 2 3)". You need either "(list 1 2 3)" or the odd syntax "'(1 2 3)". |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
43 Lists can then be operated on with "first", "last", "rest" and "cons" (construct from head and tail) functions. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
44 There are also functions like "nth" (nth <list> <0-based index>) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
45 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
46 Vectors use sequare brackets. They're like a list, but you can implicitly index them (i.e. you don't need "nth") by doing ([vector] pos) because they're treated as functions. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
47 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
48 first, last and rest can be used for pattern matching. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
49 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
50 Sets use hash followed by squiggly brackets (which is easily confused with a map). Sorted sets use "(sorted-set <values> …)". |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
51 Manipulating sets is less clean - (clojure.set/union <set1> <set2>) for unioning sets and (clojure.set/difference <set1> <set2>) for difference. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
52 Sets are also functions and so you can check if an item is in a set with (#{set} <value>). Returns "nil" if not in the set, else returns the value. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
53 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
54 Maps are defined with squiggly brackets and have implicit key-value positions, so you can write"{:chewie :wookie :lea :human}" for keys :chewie and :lea with values :wookie and :human. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
55 You can also put in commas, though. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
56 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
57 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
58 ":text" appears to be a unique identifier ("keyword"), but the book doesn't mention this until three pages after it started using it. They're like Ruby symbols and Prolog/Erland atoms. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
59 Symbols point to something else and keywords point to themselves (although it isn't made clear what that means). |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
60 Keywords are also functions, so (map key) and (key map) are equivalent. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
61 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
62 Maps can be merged with "merge" or with "merge-with", which takes a function to calculate the merged value. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
63 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
64 Variables are assigned to with "(def <variable-name> <value>)". Functions are defined with "(defn <func-name> [<params list>] (<body>))". |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
65 There's also an optional docstring after <func-name>. Docs can be read with "(doc <func-name>)". |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
66 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
67 Function arguments can be quite complex and use pattern matching, e.g. getting the second value in a vector with: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
68 (defn line-end [[_ second]] second) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
69 which takes a single arg (the vector "[_ second]") and the body just returns "second". Clojure calls this "destructuring". It ignores any additional values (e.g. "[_ c]" matches "[1 2 3 4 5]" and puts "2" in c and ignores the trailing 3,4,5). |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
70 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
71 Where "def" is used for long-term variables (which are created in user-space - the return result is #'user/<def-name>), "let" is used for local variables, which is useful within function definitions, e.g.: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
72 (defn centre_space [board] (let [[_ [_ c]] board] c)) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
73 which is a function with one argument (board) where the function body uses "let" with a vector containing a pattern to match and a variable to match against, and then a function that uses that vector. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
74 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
75 There are also anonymous functions that use "(fn [args] (body))" - like a defn, but without a name. Alternatively, "#(function-body-with-%-as-a-variable)" defines an anonymous "reader macro" function with % bound to each value. |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
76 (map (fn [w] (* 2 (count w))) my_list) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
77 is equivalent to: |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
78 (map #(* 2 (count %)) my_list) |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
79 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
80 As well as the map function, there's "apply" and "filter". |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
81 |
29025305bbb7
Add notes and exercises from Day 1 of Clojure
IBBoard <dev@ibboard.co.uk>
parents:
diff
changeset
|
82 Also, some helper functions exist, like "odd?". |