Wednesday, November 9, 2011

Getting to Know Let

This write-up follows along with Brian Harvey's CS61A 2008 SICP class, lecture 4.

Note that these are not good examples of idiomatic Clojure coding style, but more of an introduction to functional programming using Clojure.

Think about the following three patterns of computation.

(TODO: translate these function from scheme to clojure, and add pic for clarity.)
---
1. every - call for each item
---
2. keep - keeps some
---
3. accumulate - smooshes together
---

In other languages these patterns might be the sort of thing you may be accustomed to performing in an inerative loop. instead of iteration we describe this process as a transformation.

These are all common things one might want to do to a set. They are known as higher order functions.

This is where lambda comes into play. We can better understand higher order functions and lambda by destructuring with let.

Let's take a closer look at the 'let' special form using the qudratic formula as an example.


first let's import a subset of the clojure.contrib library so we can use the sqaure root (sqrt) function
(ns test
  (:use clojure.contrib.generic.math-functions))

(TODO: add pic of quadratic formula.)
basic quadratic formula as a function
(defn roots [a b c]
  (println "roots: " str 
         (/ (+ (- b)(sqrt (- (* b b) (* 4 a c)))) (* 2 a))
         (/ (- (- b)(sqrt (- (* b b) (* 4 a c)))) (* 2 a)) ))
(roots 1 -5 6)

annoying that you have to name the roots1 function in this example
(defn roots-refactor-1 [a b c]
  (defn roots1 [d]
                (println "roots-refactor-1: " str
                       (/ (+ (- b) d) (* 2 a))
                       (/ (- (- b) d) (* 2 a)) ))
    (roots1 (sqrt (- (* b b) (* 4 a c)))) )
(roots-refactor-1 1 -5 6)

example of lamba, an anonymous function, which is passed in data (3 2 1)
(#(+ %1 %2 %3) 3 2 1)

lambda expression version which uses an anonymous function
(defn roots-refactor-2 [a b c]
        ( #(println "root-refactor-2: " str 
            (/ (+ (- b) %) (* 2 a))
            (/ (- (- b) %) (* 2 a)) )
         (sqrt (- (* b b) (* 4 a c))) ))
(roots-refactor-2 1 -5 6)               

the let form is syntactic sugar -- shorthand
let is the syntactic rearrangement of lambda and an invocation of that function
the secret: let = lambda + ()
(defn roots-refactor-3 [a b c]
        (let [d  (sqrt (- (* b b) (* 4 a c)))]
          (println "root-refactor-3: " str 
            (/ (+ (- b) d) (* 2 a))
            (/ (- (- b) d) (* 2 a))) ))
(roots-refactor-3 1 -5 6)

let abbreviates a lambda and invokes the function with arguments these are stored as local variables which will change every time it is called by the wrapper function
(defn roots-refactor-4 [a b c]
        (let [d  (sqrt (- (* b b) (* 4 a c))) ;d is the name of a local variable
              -b (- b) ;same with -b, just a variable name
              two*a (* 2 a)] ;yep two*a is just a variable too
          (println "root-refactor-4: " str 
            (/ (+ -b d) two*a)
            (/ (- -b d) two*a)) ))
(roots-refactor-4 1 -5 6)

Remember: let is a 2 step operation, one to create a lambda and two to invoke that procedure with arguments

No comments:

Post a Comment