Skip to content

Instantly share code, notes, and snippets.

@leontalbot
Last active July 27, 2016 22:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leontalbot/87bea345d34ad4995ed2f8996e1cd986 to your computer and use it in GitHub Desktop.
Save leontalbot/87bea345d34ad4995ed2f8996e1cd986 to your computer and use it in GitHub Desktop.
;; Go to: http://bit.do/clj-mtl-meetup-2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Install a Clojure Editor
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The two favorite editors in the Clojure community are Emacs (cider) and IntelliJ (Cursive).
;; However, for the sake of the experiment, we are going to use Light Table, which is really
;; simple to grasp. Light Table has been developed by Chris Granger, a notable Clojure developer.
;; Installation steps
;; 1. Got to http://lighttable.com/ and click the "download" link
;; 2. Install and open the app.
;; 3. Open this file, meetup-2.clj using light table.
;; 4. Go at the end of the following line and hit Cmd + Enter (or CRTL + Enter for PCs)
(str "Hello World!")
;; 5. Wait for about 1-2 minutes (yeah, JVM startup time is long, one of the only downsides of Clojure.)
;; 6. See the result of the evaluated expression, inline.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Learn some Clojure syntax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Comments start with semicolons.
;; Clojure is written in "forms", which are just
;; lists of things inside parentheses, separated by whitespace.
;;
;; The clojure reader assumes that the first thing is a
;; function or macro to call, and the rest are arguments.
;; The first call in a file should be ns, to set the namespace
;; (ns learnclojure)
;; More basic examples:
;; str will create a string out of all its arguments
(str "Hello" " " "World")
;; Math is straightforward
(+ 1 2 3)
(- 2 1)
(* 1 2)
(/ 2 1)
;; Equality is =
(= 1 1)
(= 2 1)
;; You need not for logic, too
(not true)
;; Nesting forms works as you expect
(+ 1 (- 3 2)) ; the same as = 1 + (3 - 2)
;;;; Collections & Sequences
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Lists are linked-list data structures, while Vectors are array-backed.
;; Vectors and Lists are java classes too!
(class [1 2 3])
(class '(1 2 3))
;; A list would be written as just (1 2 3), but we have to quote
;; it to stop the reader thinking the first element is a function.
;; Also, (list 1 2 3) is the same as '(1 2 3)
;; "Collections" are just groups of data
;; Both lists and vectors are collections:
(coll? '(1 2 3))
(coll? [1 2 3])
;; "Sequences" (seqs) are abstract descriptions of lists of data.
;; Only lists are seqs.
(seq? '(1 2 3))
(seq? [1 2 3])
;; A seq need only provide an entry when it is accessed.
;; So, seqs which can be lazy -- they can define infinite series:
(range 4)
;;(an infinite series) (range) ;; do not eval, it is an inifinite loop!
(take 4 (range))
;; Use cons to add an item to the beginning of a list or vector
(cons 4 [1 2 3])
(cons 4 '(1 2 3))
;; Conj will add an item to a collection in the most efficient way.
;; For lists, they insert at the beginning. For vectors, they insert at the end.
(conj [1 2 3] 4)
(conj '(1 2 3) 4)
;; Use concat to add lists or vectors together
(concat [1 2] '(3 4))
;; Use filter, map to interact with collections
(map inc [1 2 3])
(filter even? [1 2 3])
;; Use apply to apply a function on a sequence
(apply + [1 2 3 4])
;; = (+ 1 2 3 4)
;; Use reduce to reduce a collection
(reduce + [1 2 3 4])
;; = (+ (+ (+ 1 2) 3) 4)
;; Reduce can take an initial-value argument too
(reduce conj [] '(3 2 1))
;; = (conj (conj (conj [] 3) 2) 1)
;; Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Use fn to create new functions. A function always returns
;; its last statement.
(fn [] "Hello World")
;; (You need extra parens to call it)
((fn [] "Hello World"))
;; You can create a var using def
(def x 1)
x
;; Assign a function to a var
(def hello-world (fn [] "Hello World"))
(hello-world)
;; You can shorten this process by using defn
(defn hello-world [] "Hello World")
;; The [] is the list of arguments for the function.
(defn hello [name]
(str "Hello " name))
(hello "Steve")
;; You can also use this shorthand to create functions:
;;(def hello2 #(str "Hello " %1))
;;(hello2 "Fanny")
;; You can have multi-variadic functions, too
(defn hello3
([] "Hello World")
([name] (str "Hello " name)))
(hello3 "Jake")
(hello3)
;; Functions can pack extra arguments up in a seq for you
(defn count-args [& args]
(str "You passed " (count args) " args: " args))
(count-args 1 2 3)
;; You can mix regular and packed arguments
(defn hello-count [name & args]
(str "Hello " name ", you passed " (count args) " extra args"))
(hello-count "Finn" 1 2 3)
;;;; Maps
;; Hash maps and array maps share an interface. Hash maps have faster lookups
;; but don't retain key order.
(class {:a 1 :b 2 :c 3})
(class (hash-map :a 1 :b 2 :c 3))
;; Arraymaps will automatically become hashmaps through most operations
;; if they get big enough, so you don't need to worry.
;; Maps can use any hashable type as a key, but usually keywords are best
;; Keywords are like strings with some efficiency bonuses
(class :a)
(def stringmap {"a" 1, "b" 2, "c" 3})
;;stringmap
(def keymap {:a 1, :b 2, :c 3})
keymap
;; By the way, commas are always treated as whitespace and do nothing.
;; Retrieve a value from a map by calling it as a function
(stringmap "a")
(keymap :a)
;; Keywords can be used to retrieve their value from a map, too!
(:b keymap)
;; Don't try this with strings.
("a" stringmap)
;; Retrieving a non-present key returns nil
(stringmap "d")
;; Use assoc to add new keys to hash-maps
(def newkeymap (assoc keymap :d 4))
newkeymap
;;But remember, clojure types are immutable!
;;Try eval back keymap
keymap
;; Use dissoc to remove keys
(dissoc keymap :a :b)
;;;; Useful forms
;; Logic constructs in clojure are just macros, and look like
;; everything else
(if false "a" "b")
(if false "a")
;; Use let to create temporary bindings
(let [a 1 b 2]
(> a b))
;; Use the threading macros (-> and ->>) to express transformations of
;; data more clearly.
;; The "Thread-first" macro (->) inserts into each form the result of
;; the previous, as the first argument (second item)
(-> {:a 1 :b 2}
(assoc :c 3)
(dissoc :b))
;; This expression could be written as:
(dissoc (assoc {:a 1 :b 2} :c 3) :b)
;; The double arrow does the same thing, but inserts the result of
;; each line at the *end* of the form. This is useful for collection
;; operations in particular:
(->> (range 10)
(map inc)
(filter odd?)
(into []))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Exercices
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Some 4clojure.com problems
;; http://www.4clojure.com/problem/15
;; Hint: fn, partial
;; http://www.4clojure.com/problem/16
;; Hint: str
;; http://www.4clojure.com/problem/20
;; Hint: first, rest, last, second
;; http://www.4clojure.com/problem/21
;; Hint: drop
;; http://www.4clojure.com/problem/22
;; Hint: reduce, apply, constantly
;; http://www.4clojure.com/problem/24
;; Hint:
;; https://www.4clojure.com/problem/26
;; Hint: take, iterate
;; https://www.4clojure.com/problem/38
;; Hint: reduce
;; https://www.4clojure.com/problem/27
;; Hint: string?, reverse, seq
;; https://www.4clojure.com/problem/29
;; Hint: re-seq
;;; Answers to exercices
;; 4clojure 15
(fn [x] (* x 2))
(partial * 2)
;; 4clojure 16
(fn [x] (str "Hello, " x "!"))
#(str "Hello, " % "!")
;; 4clojure 20
#(first (rest (reverse %)))
(comp second reverse)
;; 4clojure 21
(fn [coll pos] (first (drop pos coll)))
;; 4clojure 22
(fn [coll] (reduce + (map (constantly 1) coll)))
(fn [coll] (apply + (map (fn [x] 1) coll)))
;; 4clojure 24
(fn [coll] (apply + coll))
#(reduce + %)
;; 4clojure 26
#(take % (map first (iterate (fn [[a b]] [b (+ a b)]) [1 1])))
; ;; 4clojure 38
(fn [x & xs]
(reduce #(if (< %1 %2) %2 %1) x xs))
;; 4clojure 27
#(= (if (string? %)
(apply str (reverse %))
(reverse %))
%)
#(= (seq %) (reverse (seq %)))
;; 4clojure 29
#(apply str (re-seq #"[A-Z]+" %))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Modules
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Use require to import a module
(require 'clojure.string)
;; Use / to call functions from a module
;; Here, the module is clojure.string and the function is blank?
(clojure.string/blank? "")
;; You can give a module a shorter name on import
(require '[clojure.string :as str])
(str/replace "This is a test." #"[a-o]" str/upper-case)
;;(#"" denotes a regular expression literal)
;; You can use require (and use, but don't) from a namespace using :require.
;; You don't need to quote your modules if you do it this way.
(ns test
(:require
[clojure.string :as str]
[clojure.set :as set]))
(ns meet-up-2.text
(:require [clojure.string :as str]))
(def text
"Do not go gentle into that good night,
Old age should burn and rave at close of day;
Rage, rage against the dying of the light.
Though wise men at their end know dark is right,
Because their words had forked no lightning they
Do not go gentle into that good night.
Good men, the last wave by, crying how bright
Their frail deeds might have danced in a green bay,
Rage, rage against the dying of the light.
Wild men who caught and sang the sun in flight,
And learn, too late, they grieved it on its way,
Do not go gentle into that good night.
Grave men, near death, who see with blinding sight
Blind eyes could blaze like meteors and be gay,
Rage, rage against the dying of the light.")
(defn word-list [s] (str/split s #"\W+"))
(defn word-count [s] (count (word-list s)))
(word-count text)
(defn line-count [s] (count (str/split s #"\n")))
(line-count text)
(defn find-avg-word-length [a-string]
(let [words (word-list a-string)
wcount (count words)
sum-lengths (reduce + (map count words))
avg-length (double (/ sum-lengths wcount))]
avg-length))
(find-avg-word-length text)
(defn simple-find-longest [a-string]
(let [words (word-list a-string)
lengths (map count words)
length-word-map (zipmap lengths words)]
(last (sort-by key length-word-map))))
(defn faster-find-longest [a-string]
(let [words (word-list a-string)
lengths (map count words)
length-word-map (zipmap lengths words)]
(apply max-key (fn [[length word]] length) length-word-map)))
(defn words-frequency [a-string]
(let [words (word-list (str/lower-case a-string))]
(sort-by last (frequencies words))))
(defn frequency-of [a-word a-string]
(let [a-word-lc (str/lower-case a-word)]
(->> a-string
str/lower-case
word-list
(filter #(= a-word-lc %))
count)))
(word-count text)
(line-count text)
(find-avg-word-length text)
(simple-find-longest text)
(faster-find-longest text)
(words-frequency text)
(frequency-of "of" text)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Java Interop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Java has a huge and useful standard library, so
;; you'll want to learn how to get at it.
;; Use import to load a java module
(import java.util.Date)
;; You can import from an ns too.
#_(ns test
(:import java.util.Date
java.util.Calendar))
;; Use the class name with a "." at the end to make a new instance
(Date.) ;; <a date object>
;; Use . to call methods. Or, use the ".method" shortcut
(. (Date.) getTime) ;; <a timestamp>
(.getTime (Date.)) ;; exactly the same thing.
;; Use / to call static methods
(System/currentTimeMillis) ;; <a timestamp> (system is always present)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment