Def, Symbols, and Vars
Access a symbol using ':
(def album "Parallels")
(str 'album) ; "album"Just like keywords, symbols stand for some values. Unlike keywords, which only stand for themselves, symbols stand for some other value.
Compare symbols and their values with one another:
(def artist "Iron Maiden")
(def song "Iron Maiden")
(= 'artist 'song) ; false
(= artist song) ; trueNote the difference between the comparison of the symbols and their values.
Get hold of a var using #':
(def song "Pale Fire")
#'song ; #'user/songA var is a bindig of a value ("Pale Fire") to a symbol ('song).
Get the value and the symbol behind a var:
(def song "Monument")
(.get #'song) ; "Monument"
(.-sym #'song) ; songWith .get, the get method is invoked; with .-sym, the sym field is
accessed. See also Interoperating with Java.
Create a dynamic var:
(def ^:dynamic *verbose* false)By convention, dynamic vars are surrounded by earmuffs (*…*).
Bind the values of dynamic vars:
(binding [*verbose* true] (println "verbose?" *verbose*)) ; verbose? trueThe var *verbose* hast the value true withing the binding scope and in all
the functions that are called within that scope.
Change a dynamic var from inside the binding:
(binding [*verbose* true]
(set! *verbose* false)
(println "verbose?" *verbose*)) ;; verbose? falseUnlike def, let does not create any vars:
(let [foo "bar"] (println #'foo)) ; Unable to resolve var: foo in this contextThe REPL provides additional dynamic vars, such as *1, *2 for the last,
second last, etc. result, and *e for the last exception.
Exercises
Verbose Fibonacci
Write a function fib that expects a parameter n. The function shall compute
the n-th Fibonacci number (see chapter
5), but write a
recursive function without tail-calls.
Define a dynamic var *verbose*, which is set to false by default. Enhance
the function fib so that it logs it calls in the form fib(n)=x, with n
being the argument for n, and x the calculated result.
Define another function do-fib that, besides the n parameter, also accepts a
parameter debug. The original fib function then shall be invoked while
setting the *verbose* flag to the value of debug.
Hint: Use binding so set the *verbose* var temporarely.
Test: (do-fib 4 false) shall return 5 and output nothing. (do-fib 3 true)
shall return 3 and output the following:
fib(1)=1
fib(0)=1
fib(1)=1
fib(2)=2
fib(3)=3Memoized Fibonacci
Create a dynamic binding *cache* that is initialized to an empty map.
Write a memoized implementation of a function that computes the nth Fibonacci
number, expecting the parameter n. Memoization is an optimization that works
as follows:
- For the arguments
0and1, alsways return1. - If the parameter
nis a key in the map, return the value associated with it. - Otherwise, calculate the result
n, and store it in the map.
Hint: Handle the three cases using cond. For the third case, first compute
(fib (- n 2)) and cache its result using binding. Then calculate (fib (- n 1)), which now can make use of the cache.
Test: (fib 42) shall return 433494437—and finish within a second.
Verbose Memoized Fibonacci
Combine the solutions of the two previous exercises to enable debug output of
the memoized Fibonacci function. Write a function (do-fib n debug) that turns
verbose output on and off based on the value of debug. The output shall
indicate the argument n, the result of the computation (fib n), and whether
or not the cache was hit or missed.
Hint: Use two dynamic vars: *cache* and *verbose*.
Test: (do-fib 6 true) shall return 13 and output:
fib(2)=2 cache hit
fib(2)=2 cache missed
fib(3)=3 cache hit
fib(4)=5 cache hit
fib(2)=2 cache hit
fib(3)=3 cache hit
fib(4)=5 cache missed
fib(5)=8 cache hit
fib(6)=13 cache hit
13