23
Feb 7, 2015 Clojure 2

Feb 7, 2015 Clojure 2. 3 Functions The syntax to define a named function is: (defn function_name [ arguments ] expressions ) The value of the function

Embed Size (px)

Citation preview

Feb 7, 2015

Clojure 2

3

Functions

The syntax to define a named function is:(defn function_name [arguments] expressions) The value of the function is the value of the last expression

evaluated

The syntax of a function call is(function arguments) Notice that the function being called is the first thing inside

the parentheses This need not be the name of a function; it can be any

expression that results in a function

Tail recursion (Erlang)

Non-tail recursive function to find the length of a list:len([]) -> 0;len([_ | T]) -> 1 + len(T).

Tail recursive function to find the length of a list:len(L) -> len(0, L).

len(N, []) -> N;len(N, [_ | T]) -> len(N + 1, T).

Tail recursion (Clojure)

Non-tail recursive function to find the length of a list:(defn len-1 [lst] (if (empty? lst) 0 (inc (len-1 (rest lst))) ) )

Tail recursive function to find the length of a list:(defn len-2 ([lst] (len-2 0 lst)) ([n lst] (if (empty? lst) n (len-2 (inc n) (rest lst))) ) )

recur

The previous function, len-2, is tail-recursive, but the compiler doesn’t optimize it into a loop

Clojure runs on the JVM, which doesn’t optimize tail recursion

Workaround:(defn len-2 ([lst] (len-2 0 lst)) ([n lst] (if (empty? lst) n (recur (inc n) (rest lst))) ) )

Tail recursion (Erlang)

Non-tail recursive function to find the factorial:

factorial(1) -> 1;factorial(N) -> N * factorial(N - 1).

Tail recursive function to find the factorial:

factorial(N) -> factorial(1, N).

factorial(Acc, 1) -> Acc;factorial(Acc, N) -> factorial(N * Acc, N - 1).

Tail recursion (Clojure) Non-tail recursive function to find the factorial:(defn factorial-1 [n] (if (= n 1) 1 (* n (factorial-1 (dec n))) )

Tail recursive function to find the factorial:(defn factorial-2 ([n] (factorial-2 1 n)) ([acc n] (if (= n 1) acc (recur (* n acc) (dec n)) ) ) )

Loop version of factorial

(def factorial (fn [n]

(loop [cnt n acc 1] (if (zero? cnt)

acc (recur (dec cnt) (* acc

cnt))))))

Lists vs. vectors

Lists uses (a b c) syntax, vectors use [a b c] Lists do standard Lisp evaluation (evaluate arguments,

then apply function in first position to them). Vectors evaluate to themselves. Lists use the cons cell representation we have seen. Vectors use an internal representation that more

efficiently supports extension.

Lists vs. vectors (continued)

(def mylist '(a b c d))

user=> (cons 'q mylist)(q a b c d) ;; but mylist is unchanged

(def myvec [a b c d])

user=> (conj myvec 'q)[a b c d q] ;; but myvec is unchanged

Vectors as stacks

(def mystack [1 2 3])

user=> (peek mystack)3

user=> (pop mystack)[1 2] ;; but mystack is unchanged

user=> (conj mystack 4)[1 2 3 4] ;; but mystack is still unchanged

map (def fruit '((apple red) (banana yellow) (cherry

red)))

user=> (map first fruit)(apple banana cherry)

(defn my-map [f lst] (if (empty? lst) () (cons (f (first lst)) (my-map f (rest lst))) ) )

user=> (map my-first fruit)(apple banana cherry)

Map using tail recursion

(defn strict-map1 [f coll]

(loop [coll coll, acc nil]

(if (empty? Coll)

(reverse acc)

(recur (next coll)

(cons (f (first coll))

acc)))))

Map using vectors

(defn strict-map2 [f coll]

(loop [coll coll, acc []]

(if (empty? Coll)

acc

(recur (next coll)

(conj acc

(f (first coll))))))

Conj vs. cons

The “right” way to add an element to any sequence in Clojure is conj.

It always adds elements in the most efficient way. (cons 1 '(2 3)) => (1 2 3) (conj '(2 3) 1) => (1 2 3) (conj [2 3] 1) => [2 3 1]

Anonymous functions

An anonymous function has the syntax: (fn [parameters] body)

Example: (fn [x] (* x x))

filter (def fruit '((apple red) (banana yellow) (cherry

red)))

user=> (filter (fn [x] (= (second x) 'red)) fruit)((apple red) (cherry red))

(defn my-filter [p lst] (cond (empty? lst) () (p (first lst)) (cons (first lst) (my-filter p (rest lst))) :else (my-filter p (rest lst)) ) )

user=> (my-filter (fn [x] (= (second x) 'red)) fruit)((apple red) (cherry red))

Speaking of maps…

A map or hash is a sequence of key/value pairs, enclosed in braces, for example,{:ace 1, :deuce 2, "trey" 3} Elements are separated by whitespace or commas It is helpful to use commas between key/value pairs

A map is also a function: user=> (def cards {:ace 1, :deuce 2, "trey" 3})#'user/cards

user=> (cards :deuce)2

Keywords are also functions: user=> (:deuce cards)2

Immutability and Laziness

Two key ideas in Clojure. Immutable objects never change once they are created. Why would Clojure do this?

Invariants can be handled just at construction time. Reasoning about possible states is simplified. Equality has persistent meaning. Sharing is cheap. Just send a reference. Fosters concurrent programming.

Structural Sharing

(def baselist (list :barnabas :adam))

(def lst1 (cons :willie baselist))

(def lst2 (cons :phoenix baselist))

(= (next lst1) (next lst2)) ; true

(identical? (next lst1) (next lst2))

;; also true

Simple tree example

Demonstrates more complex structural sharing. xconj from pgs. 120-123 in Joy

The End