[Cs254f11] anonymous functions

Lee Spector lspector at hampshire.edu
Mon Sep 26 20:48:30 EDT 2011


;; The syntax for anonymous functions came up in class today and I thought
;; I'd send out an example to clarify some things (with a fancy/advanced
;; higher-order functional-programming cool thing at the end). I'm using 
;; a musical example just because of another conversation in class today,
;; but I think that it should be clear enough what's going on even if you 
;; don't know anything about music theory.

;; Define c-major as a sequence of MIDI note numbers starting with a C at 0:

(def c-major [0 2 3 4 6 8 10 11])

; #'user/c-major

c-major

; [0 2 3 4 6 8 10 11]

;; Define a function to transpose a note up a minor third:

(defn up-a-minor-third
  [note]
  (+ note 3))

; #'user/up-a-minor-third

(up-a-minor-third 5)

; 8

;; We can use map to apply a function to every element of a sequence:

(map up-a-minor-third c-major)

; (3 5 6 7 9 11 13 14)

;; But suppose we hadn't written up-a-minor-third, which is kind of simple
;; to bother defining as a whole function. And besides, we may want to
;; transpose by other intervals too. We can use an anonymous function to 
;; stick the whole definition where a function name usually is:

((fn [note] (+ note 3)) 5)

; 8

;; Make sure you note all of the parentheses above. The first thing in the 
;; overall list is itself a list that looks like "(fn [note] (+ note 3))".
;; That is a function of one argument (called note) that returns the value 
;; of its argument + 3.

;; Of course there's no reason ever to do exactly that thing above because
;; after all it's simpler just to do (+ 3 5)! So you won't see exactly 
;; that. But anonymous functions can be very handy when we want to pass
;; a function to another function, like map:

(map (fn [note] (+ note 3)) c-major)

; (3 5 6 7 9 11 13 14)

;; And when your anonymous function is particularly simple then it's often
;; even nicer to use the #() abbreviation:s

(map #(+ % 3) c-major)

; (3 5 6 7 9 11 13 14)

;; And we can do it for any interval, so instead of up-an-octave we
;; can do:

(map #(+ % 12) c-major)

; (12 14 15 16 18 20 22 23)

;; Note however that you can't nest #() forms, and there are also some
;; situations in which they don't work very well... So use #() for short 
;; and simple anonymous functions but use the (fn ...) syntax for anything
;; complex.

;; ADVANCED ADDENDUM:
;; Something even fancier that is sometimes handy is to use, in place of
;; an anonymous function, is a function that RETURNS an anonymous function:

(defn transposer
  [interval]
  (fn [note]
    (+ note interval)))

; #'user/transposer

;; A call to transposer takes an interval and returns a function that takes
;; one argument and adds the interval to the argument:

(transposer 3)

; #<user$transposer$fn__112 user$transposer$fn__112 at 7217fef>

((transposer 3) 10)

; 13

;; The nice thing about this is that you can get a function for any interval,
;; and you don't have to explicitly write the transposer code each time 
;; (which would be a bigger deal if we were doing something more complicated  
;; than transposing, which is after all just one addition):

((transposer 100) 23)

; 123

;; And of course this is handier when used as an argument to another function:

(map (transposer 3) c-major)

; (3 5 6 7 9 11 13 14)

--
Lee Spector, Professor of Computer Science
Cognitive Science, Hampshire College
893 West Street, Amherst, MA 01002-3359
lspector at hampshire.edu, http://hampshire.edu/lspector/
Phone: 413-559-5352, Fax: 413-559-5438



More information about the Cs254f11 mailing list