Computing with Clojure

Press T to toggle slide-show mode.

Table of Contents

1 Computing with Clojure    slide

Alan Dipert & Clinton Dreisbach
Relevance, Inc.
thinkrelevance.com

2 Welcome

2.1 Welcome    slide
  • Schedule
    • 0900-1030 Introduction to Clojure - REPL, functions, Java + exercises
    • 1030-1100 Break
    • 1100-1230 Namespaces, Collections, Flow Control
    • We are making this up. Who knows? There will be a break.

3 Tutorial

3.1 Clojure Overview & REPL    slide

3.1.1 Clojure Rationale

3.1.1.1 Clojure Objectives    slide
  • Lisp: small core, code-as-data, abstraction
  • Functional, emphasis on immutability
  • Support concurrency & managed state
  • Expose and embrace host platforms
3.1.1.2 Why Another Lisp?    slide
  • Limits to change post standardization
  • Core data structures mutable, not extensible
  • No concurrency in specs
  • Standard Lisps are their own platforms
  • Good implementations already exist for JVM
3.1.1.3 Why the JVM?    slide
  • VM, not OS, is target platform of the future
    • Type system
    • Dynamic enforcement and safety
    • Garbage collection
    • Libraries
    • Bytecode, just-in-time compilation

3.1.2 Evaluation and the REPL

3.1.2.1 The REPL    slide
user=> (+ 3 4)  ; Read
                ; Eval
7               ; Print
user=>          ; Loop
3.1.2.2 Traditional Evaluation    slide

slide-assets/traditional-evaluation.svg

3.1.2.3 Clojure Evaluation    slide

slide-assets/clojure-interactive-evaluation.svg

3.1.3 Basic Syntax

3.1.3.1 Operation Forms    slide
(op ... )
  • op can be either:
    • special operator or macro
    • expression which yields a function
      • more generally, something invocable
3.1.3.2 Structure vs. Semantics    slide

slide-assets/structure-and-semantics.svg

3.1.3.3 Literals (1)    slide
42           ; Long
6.022e23     ; Double

42N          ; BigInt
1.0M         ; BigDecimal
22/7         ; Ratio

"hello"      ; String
\e           ; Character
3.1.3.4 Literals (2)    slide
true  false        ; Booleans

nil                ; null

+  Fred  *bob*     ; Symbols

:alpha  :beta      ; Keywords
3.1.3.5 Data Structures    slide
(4 :alpha 3.0)     ; List

[2 "hello" 99]     ; Vector

{:a 1, :b 2}       ; Map

#{alice jim bob}   ; Set
  • Note: commas are whitespace
3.1.3.6 Reader Macros    slide
Reader MacroExpansion
'foo(quote foo)
#'foo(var foo)
@foo(deref foo)
#(+ % 5)(fn [x] (+ x 5))

3.1.4 Discoverability of the Environment

3.1.4.1 doc    slide
user=> (use 'clojure.repl)

user=> (doc when)
;; -------------------------
;; clojure.core/when
;; ([test & body])
;; Macro
;;   Evaluates test. If logical true, evaluates
;;   body in an implicit do.
;;=> nil
3.1.4.2 find-doc    slide
user=> (find-doc "sequence")
;; ... all definitions with "sequence"
;; in their documentation ...
;;=> nil
3.1.4.3 apropos    slide
user=> (apropos "map")
;;=> (sorted-map ns-unmap zipmap map mapcat
;;  sorted-map-by map? amap struct-map
;;  proxy-mappings pmap map-indexed
;;  ns-map array-map hash-map)
3.1.4.4 source    slide
user=> (source take)
;; (defn take
;;   "Returns a lazy sequence of the first n items
;;   in coll, or all items if there are fewer than n."
;;   {:added "1.0"
;;    :static true}
;;   [n coll]
;;   (lazy-seq
;;    (when (pos? n)
;;      (when-let [s (seq coll)]
;;       (cons (first s) (take (dec n) (rest s)))))))
;;=> nil
3.1.4.5 dir    slide
user=> (dir clojure.repl)
;; apropos
;; demunge
;; dir
;; dir-fn
;; doc
;; find-doc
;; pst
;; root-cause
;; set-break-handler!
;; source
;; source-fn
;; stack-element-str
;; thread-stopper
;;=> nil

3.1.5 Exercises

3.1.5.1 Exercise: Basic Math    slide
  • Find the product of 314 and 159 using the REPL.
3.1.5.2 Exercise: Basic Math Solution    slide
  • Find the product of 314 and 159 using the REPL.
    user=> (* 314 159)
    ;=> 49926
    
3.1.5.3 Exercise: Prefix Notation    slide
  • Transform the following expression into Clojure code: (42 + 17 * 9) / 12
3.1.5.4 Exercise: Prefix Notation Solution    slide
  • Transform the following expression into Clojure code: (42 + 17 * 9) / 12
    user=> (/ (* (+ 42 17) 9) 12)
    ;=> 177/4
    

3.2 Functions    slide

3.2.1 Motivation    slide

  • Clojure is a functional language
  • Functions are a first-class abstraction
  • Ubiquitous support for high-order functions
  • Core code (almost) entirely pure functions
    • i.e., no side-effects
  • The obvious place to start…

3.2.2 Functions    slide

  • Functions are first-class abstractions in Clojure
    • Can be stored, passed as argument, invoked
  • fn creates a function with named parameters and body
;;      params         body
;;     ---------  ---------------
  (fn  [message]  (print message) )

;;=> #<user$eval484$fn__485@45d1c3cd>

3.2.3 Invoking Functions    slide

  • (op ...)
  • Invoke a function with fn itself in function position
(  (fn [message] (print message))  ; Operation
   "Hello world!"                  ; Arguments
)
;; Hello world!
3.2.3.1 Instructor notes    notes
  • The extra whitespace helps show the structure
  • Normally we would not use so much whitespace

3.2.4 Naming functions    slide

  • fn makes anonymous functions
  • Store function in a named Var for later use
  • Invoke as list with name in function position
(def messenger (fn [msg] (print msg)))
;;=> #'user/messenger

(defn messenger [msg] (print msg))
;;=> #'user/messenger

(messenger "Hello world!")
;; Hello world!

3.2.5 let    slide

  • let binds symbols to immutable values
    • Values may be literals or expressions
  • Bound symbols are available in lexical scope
(defn messenger [msg]
  (let [a 7
        b 5
        c (capitalize msg)]
    (println a b c)
  )  ; end of 'let' scope
)  ; end of function
3.2.5.1 Notes    notes
  • Again, extra whitespace to show structure

3.2.6 Multi-arity functions    slide

  • Can overload function by arity (number of arguments)
  • Each arity is a list ([args*] body*)
  • One form can invoke another
(defn messenger
  ;; no args, call self with default msg
  ([] (messenger "Hello world!"))
  ;; one arg, print it
  ([msg] (print msg)))

(messenger)
;; Hello world!
(messenger "Hello class!")
;; Hello class!

3.2.7 Variadic functions    slide

  • Variadic: function of indefinite arity
    • Only one allowed when overloading on arity
  • & symbol in params
    • Next param collects all remaining args
    • Collected args represented as sequence
(defn messenger [greeting & who]
  (print greeting who))

(messenger "Hello" "world" "class")
;; Hello (world class)
3.2.7.1 Notes    notes
  • Sequences look and behave like lists

3.2.8 apply    slide

  • Invokes function on arguments
  • Final argument is a sequence
  • "Unpacks" remaining arguments from a sequence
(let [a 1
      b 2
      more '(3 4)]
  (apply f a b more))
;; this invokes (f 1 2 3 4)

3.2.9 apply    slide

;; & puts rest of args into sequence
(defn messenger [greeting & who]
  ;; apply gets args out of sequence
  (apply print greeting who))

(messenger "Hello" "world" "class")
;; Hello world class
3.2.9.1 Notes    notes
  • Similar to *splat in Ruby

3.2.10 Closures    slide

  • fn "closes" over surrounding lexical scope
    • Creates a closure
  • Closed-over references persist beyond lexical scope
(defn messenger-builder [greeting]
  (fn [who] (print greeting who))) ; closes over greeting

;; greeting provided here, then goes out of scope
(def hello-er (messenger-builder "Hello"))

;; greeting still available because hello-er is closure
(hello-er "world!")
;; Hello world!

3.2.11 Function literals    slide

  • Terse form #() for short fns defined inline
    • Single argument: %
    • Multiple args: %1, %2, %3, …
    • Variadic: %& for remaining args
;; A function to square numbers
(def square #(* % %))

;; A function to get the sum of squares
(def sum-of-squares #(+ (square %1) (square %2)))

3.2.12 Exercise: Greeting    slide

  • Make a function that takes a greeting and one to three number of people and greets them appropriately. Here's some example output.
(messenger "Hello" "Clinton")
;; Hello, Clinton!

(messenger "Hello" "Clinton" "Alan")
;; Hello, Clinton and Alan!

(messenger "Hello" "Clinton" "Alan" "all of you")
;; Hello, Clinton, Alan, and all of you!

3.2.13 str will be necessary to make this work.

3.2.14 Exercise: Greeting Solution    slide

(defn messenger
  ([greeting who] (str greeting ", " who "!"))
  ([greeting who1 who2] (str greeting ", " who1 " and " who2 "!"))
  ([greeting who1 who2 who3]
     (str greeting ", " who1 ", " who2 ", and " who3 "!")))

3.3 Names and Namespaces    slide

3.3.1 Namespace Concepts

3.3.1.1 Why Namespaces?    slide
  • Re-use common names in different contexts
    • e.g. clojure.core/replace and clojure.string/replace
  • Separate application "layers" or "components"
  • Libraries
  • Separate "public API" and "internal implementation"
3.3.1.2 Namespace-Qualified Vars    slide
;; In the namespace "foo.bar"
(defn hello [] (println "Hello, World!"))

;; In another namespace
(foo.bar/hello)   ; namespace-qualified
3.3.1.3 Namespace Operations    slide
  • Load: find source on classpath & eval it
  • Alias: make shorter name for namespace-qualified symbols
  • Refer: copy symbol bindings from another namespace into current namespace
  • Import: make Java class names available in current namespace

3.3.2 ns macro

3.3.2.1 ns Declaration    slide
  • Creates namespace and loads, aliases what you need
    • At top of file
  • Refers all of clojure.core
  • Imports all of java.lang
    ;; in file foo/bar/baz_quux.clj
    (ns foo.bar.baz-quux)
    
3.3.2.2 require    slide
  • Loads the namespace if not already loaded
    • Argument is a symbol, must be quoted
  • Have to refer to things with fully-qualified names
    (ns my-ns
      (:require clojure.set))
    ;;=> nil
    
    (clojure.set/union #{1 2} #{2 3 4})
    ;;=> #{1 2 3 4}
    
3.3.2.3 require :as    slide
  • Loads the namespace if not already loaded
    • Argument is a vector, must be quoted
  • Aliases the namespace to alternate name
    (ns my-ns
      (:require [clojure.set :as set]))
    ;;=> nil
    
    ;; "set" is an alias for "clojure.set"
    (set/union #{1 2} #{2 3 4})
    ;;=> #{1 2 3 4}
    
3.3.2.4 use    slide
  • Loads the namespace if not already loaded
    • Argument is a symbol, must be quoted
  • Refers all symbols into current namespace
  • Warns when symbols clash
3.3.2.5 use Example    slide
(ns my-ns
  (:use clojure.string))
;; WARNING: reverse already refers
;; to: #'clojure.core/reverse in
;; namespace: user, being reversed
;; by: #'clojure.string/reverse
;; ...
;;=> nil

(reverse "hello")
;;=> "olleh"
3.3.2.6 use :only    slide
  • Loads the namespace if not already loaded
    • Argument is a vector, must be quoted
  • Refers only specified symbols into current namespace
    (ns my-ns
      (:use [clojure.string :only (join)]))
    ;;=> nil
    (join "," [1 2 3])
    ;;=> "1,2,3"
    
3.3.2.7 import    slide
  • Makes Java classes available w/o package prefix in current namespace
    • Argument is a list, quoting is optional
  • Does not support aliases/renaming
  • Does not support Java's import *
    (ns my-ns
      (:import (java.io FileReader File))
    ;;=> nil
    (FileReader. (File. "readme.txt"))
    ;;=> #<FileReader ...>
    
3.3.2.8 ns Complete Example    slide
(ns name
  (:require [some.ns.foo :as foo]
            [other.ns.bar :as bar])
  (:use [this.ns.baz :only (a b c)]
        [that.ns.quux :only (d e f)])
  (:import (java.io File FileWriter)
           (java.net URL URI)))
3.3.2.9 Namespaces and Files    slide
  • For require/use to work, have to find code defining namespace
  • Clojure converts namespace name to path and looks on CLASSPATH
    • Dots in namespace name become /
    • Hyphens become underscores
  • Idiomatic to define namespace per file
3.3.2.10 Namespaces in the REPL    slide
  • in-ns switches to namespace
    • Creates namespace if it doesn't exist
  • Argument is a symbol, must be quoted
  • REPL always starts in namespace "user"
    user=> (in-ns 'foo.bar.baz)
    ;;=> nil
    foo.bar.baz=>
    

3.4 Working with Java    slide

3.4.1 Invoking Java code    slide

  • Clojure provides operational forms for Java invocation
TaskJavaClojure
Instantiationnew Widget("foo")(Widget. "foo")
Instance methodrnd.nextInt()(.nextInt rnd)
Instance fieldobject.field(.-field object)
Static methodMath.sqrt(25)(Math/sqrt 25)
Static fieldMath.PIMath/PI

3.4.2 Chaining access    slide

LanguageSyntax
javaperson.getAddress().getZipCode()
clojure(.getZipCode (.getAddress person))
clojure sugar(.. person getAddress getZipCode)
3.4.2.1 Notes    notes
  • Just a taste of macros
  • Clojure has fewer parens than Java!

3.4.3 Atomic data types    slide

TypeExampleJava equivalent
string"foo"String
character\fCharacter
regex#"fo*"Pattern
integer42Long
arbitrary-precision integer42Nclojure.lang.BigInt
double3.14159Double
arbitrary-precision decimal3.14159MBigDecimal
3.4.3.1 Notes    notes
  • Clojure types are Java types
  • clojure.lang.BigInt fixes bugs in Java BigInteger
3.4.3.2 Atomic data types    slide
TypeExampleJava equivalent
booleantrueBoolean
nilnilnull
symbolfooclojure.lang.Symbol
keyword:fooclojure.lang.Keyword
3.4.3.3 Composite data types    slide
TypeExampleJava equivalent
list(1 2 3)java.util.List*
vector[4 5 6]java.util.List*
map{:a 1 :b 2}java.util.Map*
set#{3 7 9}java.util.Set*
  • *read-only
3.4.3.4 Java methods vs functions    slide
  • Java methods are not Clojure functions
  • Can't store them, pass them as arguments
  • Can wrap them in functions when necessary
;; make a function to invoke .length on arg
(fn [obj] (.length obj))

3.4.4 Generating sound with javax.sound.midi    slide

(ns tutorial.midi
  (:import (javax.sound.midi MidiSystem Synthesizer)))

(defn play-a-note [note velocity duration]
  (with-open [synth (doto (MidiSystem/getSynthesizer) .open)]
    (let [channel (aget (.getChannels synth) 0)]
      (.noteOn channel note velocity)
      (Thread/sleep duration))))

(play-a-note 60 127 3000)

3.4.4.1 Exercise: Make Some Noise!    slide
  • Find the code in src/tutorial/midi.clj
  • Can you play a progression of notes?

3.5 Collections    slide

3.5.1 Overview

3.5.1.1 Wisdom of the Ancients    slide
  • "It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." - Alan J. Perlis
3.5.1.2 Working With Data    slide
  • Clojure provides extensive facilities for representing and manipulating data
  • Small number of data structures
  • Seq abstraction common across data structures & more
  • Large library of functions across all of them
3.5.1.3 Immutability    slide
  • The values of simple types are immutable
    • 4, 0.5, true
  • In Clojure, the values of compound data structures are immutable too
    • Key to Clojure's concurrency model
  • Code never changes values, generates new ones to refer to instead
  • Persistent data structures ensure this is efficient in time and space
3.5.1.4 Persistent Data Structures    slide
  • New values built from old values + modifications
  • New values are not full copies
  • New value and old value are both available after 'changes'
  • Collection maintains its performance guarantees for most operations
  • All Clojure data structures are persistent
3.5.1.5 Example: Linked List    slide

slide-assets/collections-linked-list-1.svg

3.5.1.6 Example: Linked List    slide

slide-assets/collections-linked-list-2.svg

3.5.1.7 Example: Binary Tree    slide

slide-assets/collections-tree-1.svg

3.5.1.8 Example: Tree Structure    slide

slide-assets/collections-structural-sharing.svg

3.5.1.9 Concrete Data Structures    slide
  • Sequential
    • List, Vector
  • Associative
    • Map, Vector
  • Both types support declarative destructuring

3.5.2 Sequential

3.5.2.1 Lists    slide
  • Singly-linked lists
()              ;=> the empty list
(1 2 3)         ; error because 1 not function
(list 1 2 3)    ;=> (1 2 3)
'(1 2 3)        ;=> (1 2 3)
(conj '(2 3) 1) ;=> (1 2 3)
3.5.2.2 Vectors    slide
  • Indexed, random-access, array-like
[]              ;=> the empty vector
[1 2 3]         ;=> [1 2 3]
(vector 1 2 3)  ;=> [1 2 3]
(vec '(1 2 3))  ;=> [1 2 3]
(nth [1 2 3] 0) ;=> 1
(conj [1 2] 3)  ;=> [1 2 3]

3.5.3 Associative

3.5.3.1 Maps    slide
  • Key => value, hash table, dictionary
{}                  ;=> the empty map
{:a 1 :b 2}         ;=> {:a 1 :b 2}
(:a {:a 1 :b 2})    ;=> 1
({:a 1 :b 2} :a)    ;=> 1
(assoc {:a 1} :b 2) ;=> {:a 1 :b 2}
(dissoc {:a 1} :a)  ;=> {}
(conj {} [:a 1])    ;=> {:a 1}
3.5.3.2 Nested Access    slide
  • Helper functions access data via path specified by keys
(def jdoe {:name "John Doe", :address {:zip 27705}})

(get-in jdoe [:address :zip]) ;=> 27705

(assoc-in jdoe [:address :zip] 27514)
;;=> {:name "John Doe", :address {:zip 27514}}

(update-in jdoe [:address :zip] inc)
;;=> {:name "John Doe", :address {:zip 27706}}
3.5.3.3 Sets    slide
  • Set of distinct values
#{}                  ;=> the empty set
#{:a :b}             ;=> #{:a :b}
(#{:a :b} :a)        ;=> :a
(conj #{} :a)        ;=> #{:a}
(contains? #{:a} :a) ;=> true
3.5.3.4 clojure.set Examples    slide
(require '[clojure.set :as set])
(set/union #{:a} #{:b})              ;=> #{:a :b}
(set/difference #{:a :b} #{:a})      ;=> #{:b}
(set/intersection #{:a :b} #{:b :c}) ;=> #{:b}

3.5.4 Destructuring

3.5.4.1 Destructuring    slide
  • Declarative way to pull apart compound data
    • vs. explicit, verbose access
  • Works for both sequential and associative data structures
  • Nests for deep, arbitrary access
3.5.4.2 Where You Can Destructure    slide
  • Destructuring works in fn and defn params, let bindings
    • And anything built on top of them
3.5.4.3 Sequential Destructuring    slide
  • Provide vector of symbols to bind by position
    • Binds to nil if there's no data
(def stuff [7 8 9 10 11]) ;=> #'user/stuff
;; Bind a, b, c to first 3 values in stuff
(let [[a b c] stuff]
  (list (+ a b) (+ b c)))
;;=> (15 17)
(let [[a b c d e f] stuff]
  (list d e f))
;;=> (10 11 nil)
3.5.4.4 Sequential Destructuring    slide
  • Can get "everything else" with &
    • Value is a sequence
(def stuff [7 8 9 10 11]) ;=> #'user/stuff
(let [[a & others] stuff]
  (println a)
  (println others))
;; 7
;; (8 9 10 11)
;;=> nil
3.5.4.5 Sequential Destructuring    slide
  • Idiomatic to use _ for values you don't care about
(def stuff [7 8 9 10 11]) ;=> #'user/stuff
(let [[_ & others] stuff] ; skip the first one
  (println others)))
;; (8 9 10 11)
;;=> nil
3.5.4.6 Associative Destructuring    slide
  • Provide map of symbols to bind by key
    • Binds to nil if there's no value
(def m {:a 7 :b 4}) ;=> #'user/m
(let [{a :a, b :b} m]
  [a b])
;;=> [7 4]
3.5.4.7 Associative Destructuring    slide
  • Keys can be inferred from vector of symbols to bind
(def m {:a 7 :b 4}) ;=> #'user/m
(let [{:keys [a b]} m]
  [a b])
;;=> [7 4]
(let [{:keys [a b c]} m]
  [a b c])
;;=> [7 4 nil]
3.5.4.8 Associative Destructuring    slide
  • Use :or to provide default values for bound keys
(def m {:a 7 :b 4}) ;=> #'user/m
(let [{:keys [a b c]
       :or {c 3}} m]
  [a b c])
;;=> [7 4 3]
3.5.4.9 Named Arguments    slide
  • Applying vector of keys to & binding emulates named args
(defn game [planet & {:keys [human-players computer-players]}]
  (println "Total players: " (+ human-players computer-players)))

(game "Mars" :human-players 1 :computer-players 2)
;; Total players: 3

3.5.5 Sequences

3.5.5.1 Sequences    slide
  • Abstraction for representing iteration
  • Backed by a data structure or a function
    • Can be lazy and/or "infinite"
  • Foundation for large library of functions
3.5.5.2 Sequence API    slide
  • (seq coll)
    • If collection is non-empty, return seq object on it, else nil
    • Can't recover input source from seq
  • (first coll)
    • Returns the first element
  • (rest coll)
    • Returns a sequence of the rest of the elements
  • (cons x coll)
    • Returns a new sequence: first is x, rest is coll
3.5.5.3 Sequences Over Structures    slide
  • Can treat any Clojure data structure as a seq
    • Associative structures treated as sequence of pairs
(def a-list '(1 2 3)) ;=> #'user/a-list

slide-assets/collections-seq-list-initial.svg

3.5.5.4 Sequence Over Structure    slide
(first a-list) ;=> 1

slide-assets/collections-seq-list-first.svg

3.5.5.5 Sequence Over Structure    slide
(second a-list) ;=> 2

slide-assets/collections-seq-list-second.svg

3.5.5.6 Sequence Over Structure    slide
(rest a-list) ; seq

slide-assets/collections-seq-list-rest.svg

3.5.5.7 Sequences Over Functions    slide
  • Can map a generator function to a seq
  • Seq is lazy, can be infinite
    • Can process more than fits in memory
(def a-range (range 1 4)) ;=> #'user/a-range

slide-assets/collections-seq-lazy-initial.svg

3.5.5.8 Sequences Over Functions    slide
(first a-range) ;=> 1

slide-assets/collections-seq-lazy-first.svg

3.5.5.9 Sequences Over Functions    slide
(second a-range) ;=> 2

slide-assets/collections-seq-lazy-second.svg

3.5.5.10 Sequences Over Functions    slide
(rest a-range) ; seq

slide-assets/collections-seq-lazy-rest.svg

3.5.5.11 Sequences in the REPL    slide
  • REPL always prints sequences with parens
    • But it's not a list!
  • Infinite sequences take a long time to print
(set! *print-length* 10) ; only print 10 things
3.5.5.12 Sequence Library    slide
  • Generators
    • list, vector, map, SQL ResultSet, Stream, Directory, Iterator, XML, …
  • Operations
    • map, filter, reduce, count, some, replace, …
  • Generators * Operations = Power!
3.5.5.13 Creating a Sequence    slide
(seq [1 2 3]) ;=> (1 2 3)  ; not a list
(range) ;=> (0 1 2 ... infinite
(range 3) ;=> (0 1 2)
(range 1 7 2) ;=> (1 3 5)
(iterate #(* 2 %) 2) ;=> (2 4 8 16 ... infinite
(re-seq #"[aeiou]" "clojure") ;=> ("o" "u" "e")
3.5.5.14 Seq in, Seq out    slide
(take 3 (range)) ;=> (0 1 2)
(drop 3 (range)) ;=> (3 4 5 ... infinite
(map #(* % %) [0 1 2 3]) ;=> (0 1 4 9) ; vector treated as seq
(filter even? (range)) ;=> (0 2 4 6 ... infinite
(apply str (interpose "," (range 3))) ;=> "0,1,2"
3.5.5.15 Using a Seq    slide
(reduce + (range 4)) ;=> 6
(reduce + 10 (range 4)) ;=> 16
(into #{} "hello") ;=> #{\e \h \l \o}
(into {} [[:x 1] [:y 2]]) ;=> {:x 1, :y 2}
(some {2 :b 3 :c} [1 nil 2 3]) ;=> :b
3.5.5.16 Adopting the Sequence Mindset    slide
  • Sequence library surface space is big
  • Most things you want to do are in there somewhere
  • If you find yourself explicitly iterating, look for a function
  • The Clojure Cheatsheet helps
  • http://clojure.org/cheatsheet
3.5.5.17 The Fibonacci Sequence    slide
(def fibs            ; define a sequence called fibs...
  (map first         ; that maps the first value of a pair across...
    (iterate         ; a lazy, infinite sequnce that's generated by...
      (fn [[a b]]    ; a function that destructures a pair of args...
        [b (+ a b)]) ; and returns the next pair in the sequence...
      [0 1])))       ; starting at [0 1]

(take 5 fibs)        ; consume as many as you'd like
;;=> (0 1 1 2 3)

3.5.6 Exercises

3.5.6.1 Exercise: What's That Song?    slide
  • Go back to midi.clj from the previous exercise.
  • Play the song in this data structure:
    (def notes
      [{:note 60 :duration 1}
       {:note 62 :duration 1}
       {:note 64 :duration 1}
       {:note 60 :duration 1}
       {:note 60 :duration 1}
       {:note 62 :duration 1}
       {:note 64 :duration 1}
       {:note 60 :duration 1}
       {:note 64 :duration 1}
       {:note 65 :duration 1}
       {:note 67 :duration 2}
       {:note 64 :duration 1}
       {:note 65 :duration 1}
       {:note 67 :duration 2}])
    
3.5.6.2 Exercise: Improve Your Greeting    slide
  • Go back to your greeting code from the functions section. Change this so it works for any amount of people.
    (messenger "Hello" "Clinton")
    ;; Hello, Clinton!
    
    (messenger "Hello" "Clinton" "Alan")
    ;; Hello, Clinton and Alan!
    
    (messenger "Hello" "Clinton" "Alan" "all of you")
    ;; Hello, Clinton, Alan, and all of you!
    
    (messenger "Hello" "Clinton" "Alan" "Rich" "all of you")
    ;; Hello, Clinton, Alan, Rich, and all of you!
    
  • last, butlast, list*, and clojure.string/join will come in very helpful in this exercise.
3.5.6.3 Exercise: Greeting Solution    slide
(defn messenger
  ([greeting who] (str greeting ", " who "!"))
  ([greeting who1 who2] (str greeting ", " who1 " and " who2 "!"))
  ([greeting who1 who2 & whos]
     (str
      greeting ", "
      (clojure.string/join ", " (butlast (list* who1 who2 whos)))
      ", and " (last whos) "!")))

3.6 Flow Control    slide

3.6.1 Introduction

3.6.1.1 Expressions in Clojure    slide
  • Everything in Clojure is an expression
    • Always returns a value
    • A block of multiple expressions returns the last value
      • E.g., let, do, fn
    • Expressions exclusively for side-effects return nil
3.6.1.2 Flow Control Expressions    slide
  • Flow control operators are expressions too
  • Composable, can use them anywhere
    • Less duplicate code
    • Fewer intermediate variables
3.6.1.3 Truthiness    slide
(if true :truthy :falsey)
;;=> :truthy
(if (Object.) :truthy :falsey) ; objects are true
;;=> :truthy
(if [] :truthy :falsey) ; empty collections are true
;;=> :truthy

(if false :truthy :falsey)
;;=> :falsey
(if nil :truthy :falsey) ; nil is false
;;=> :falsey
(if (seq []) :truthy :falsey) ; seq on empty coll is nil
;;=> :falsey
3.6.1.4 if    slide
(str "2 is " (if (even? 2) "even" "odd"))
;;=> "2 is even"

; else-expression is optional
(if (true? false) "impossible!")
;;=> nil
3.6.1.5 if/do    slide
  • Multiple expressions per branch
  • Last value in branch returned
    (if (even? 5)
      (do (println "even")
          true)
      (do (println "odd")
          false)) ;=> false
    ;; odd
    
3.6.1.6 cond    slide
  • Series of tests and expressions
  • :else expression is optional
    (cond
     test1 expression1
     test2 expression2
     ...
     :else else-expression)
    
3.6.1.7 cond    slide
(let [x 5]
  (cond
   (< x 2) "x is less than 2"
   (< x 10) "x is less than 10"))
;;=> "x is less than 10"
3.6.1.8 cond with :else    slide
(let [x 11]
  (cond
   (< x 2) "x is less than 2"
   (< x 10) "x is less than 10"
   :else "x is greater than or equal to 10"))
;;=> "x is greater than or equal to 10"

3.6.2 Flow Control Exercise

3.6.2.1 Exercise: Improve Your Greeting Again    slide
  • Go back to your greeting code. It is kind of a complex mess. Use flow control to make it awesome.
3.6.2.2 Exercise: One Greeting Solution    slide
(defn messenger [greeting & whos]
  (let [x (count whos)]
    (apply str greeting ", "
           (cond
             (= x 0) ["!"]
             (< x 3) (conj (vec (interpose " and " whos)) "!")
             :else 
               (concat (vec (interpose ", " (butlast whos))) 
                       [", and " (last whos) "!"])))))

3.6.3 Iteration

3.6.3.1 Recursion and Iteration    slide
  • Clojure provides loop and the sequence abstraction
  • loop is "classic" recursion
    • Closed to consumers, lower-level
  • Sequences represent iteration as values
    • Consumers can partially iterate
3.6.3.2 doseq    slide
  • Iterates over a sequence
    • Similar to Java's foreach loop
  • If a lazy sequence, doseq forces evaluation
    (doseq [n (range 3)]
      (println n))
    ;; 0
    ;; 1
    ;; 2
    ;;=> nil
    
3.6.3.3 doseq with multiple bindings    slide
  • Similar to nested foreach loops
  • Processes all permutations of sequence content
3.6.3.4 dotimes    slide
  • Evaluate expression n times
    (dotimes [i 3]
      (println i))
    ;; 0
    ;; 1
    ;; 2
    ;;=> nil
    
3.6.3.5 while    slide
  • Evaluate expression while condition is true
    (while (.accept socket)
      (handle socket))
    
3.6.3.6 Clojure's for    slide
  • List comprehension, NOT a for-loop
  • Generator function for sequence permutation
    (for [x [0 1]
          y [0 1]]
      [x y])
    ;;=> ([0 0] [0 1] [1 0] [1 1]) ; seq
    
3.6.3.7 with-open    slide
  • JDK7 introduces try-with-resources
  • Clojure provides with-open for similar purposes
    (require '[clojure.java.io :as io])
    (with-open [f (io/writer "/tmp/new")]
      (.write f "some text"))
    

3.7 HACK LIFE    slide

  • Clinton: @crnixon
  • Alan: @alandipert

4 End

Date: 2012-07-17 18:56:16 EDT

Author: Alan Dipert

Org version 7.8.03 with Emacs version 24

Validate XHTML 1.0