[Cs254f11] elegance of functional programming
Omri Bernstein
ob08 at hampshire.edu
Fri Oct 7 14:24:46 EDT 2011
And here's a neat trick to get the absolute notes back from the intervals
list:
(def myintervals [2 2 1 2 2 2 1 -1 -2 -2 -2 -2 -1 -2])
(reductions + 60 myintervals)
>> (60 62 64 65 67 69 71 72 71 69 67 65 63 62 60)
The "reduce" function takes a function (let's call it f) an optional
starting value (val) and a collection (coll). It then returns the evaluation
of something that would look like this:
(f
(f
(f
val
(nth coll 1))
(nth coll 2))
(nth coll 3) ...)
For all the elements in coll. The "reductions" function returns a list of
the intermediates, equivalent to something like this:
[ (f val (nth coll 1))
(f (f val (nth coll 1)) (nth coll 2))
(f (f (f val (nth coll 1)) (nth coll 2)) (nth coll 3))
... ]
-Omri
On Thu, Oct 6, 2011 at 2:53 PM, Lee Spector <lspector at hampshire.edu> wrote:
>
> Genetic Programmers,
>
> I was thinking about some music-related programming examples both because a
> couple of you are working on music and because musical manipulation of
> sequences of numbers (which can be pitches or rhythms etc) provides nice
> examples in general, and one function that I thought would be handy is an
> "intervals" function that returns the differences between successive notes.
> (For the non-musicians, the most important thing about a melody is the
> intervals, not the notes themselves; if you use the same intervals but start
> on a different first note then it'll still sound like the same melody, just
> in a different key.)
>
> In other words, if you have something like:
>
> (def mynotes [60 62 64 65 67 69 71 72 71 69 67 65 63 62 60])
>
> then you'd like (intervals mynotes) to return:
>
> (2 2 1 2 2 2 1 -1 -2 -2 -2 -2 -1 -2)
>
> My first thought (but not my best one -- that's at the bottom!) was to do
> this using the standard Lisp "recurse down a list" pattern. We'd first ask
> if we were given less than 2 notes, and if so then return an empty list
> (since there are no intervals if you don't have at least 2 notes). If that
> wasn't the case then we'd return the result of tacking the first interval --
> which is the second note minus the first note -- onto the result of calling
> intervals (recursively) on all of the notes except the first. Here's what
> that looks like:
>
> (defn intervals
> [notes]
> (if (< (count notes) 2)
> ()
> (cons (- (second notes) (first notes))
> (intervals (rest notes)))))
>
> It works fine, although this kind of recursion is frowned upon in Clojure,
> because Clojure (unlike some other Lisps) can't optimize away the recursion
> and so this will run out of memory for recursive calls if you give it a
> really big list (although it'd have to be very big before you failed).
>
> So here's a version that uses a similar strategy except that it uses
> Clojure's loop/recur structure instead of ordinary recursion.
>
> (defn intervals
> [notes]
> (loop [remaining notes
> result ()]
> (if (< (count remaining) 2)
> result
> (recur (rest remaining)
> (concat result
> (list (- (second remaining)
> (first remaining))))))))
>
> Also works, but pretty messy.
>
> Then I remembered that Clojure has a fancy "partition" function that can do
> lots of things (check its docs), including, if given the right arguments,
> giving us overlapping pairs of a sequence: the first and the second, the
> second and the third, the third and the fourth, etc. If I do this then I can
> map a function down those pairs that subtracts the first element from the
> second element, producing a list of the intervals:
>
> (defn intervals
> [notes]
> (map (fn [[n1 n2]] (- n2 n1))
> (partition 2 1 notes)))
>
> Better, yes? But then I smacked my forehead (not literally, in fact I was
> driving at the time and that would have been dangerous :-), and remembered
> that, as we mentioned in class yesterday, when you map down more than one
> list it stops as soon as the shortest one is exhausted. Voila:
>
> (defn intervals
> [notes]
> (map - (rest notes) notes))
>
> Nifty!
>
> -Lee
>
>
> --
> 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
>
> _______________________________________________
> Cs254f11 mailing list
> Cs254f11 at lists.hampshire.edu
> https://lists.hampshire.edu/mailman/listinfo/cs254f11
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.hampshire.edu/pipermail/cs254f11/attachments/20111007/7b8c550d/attachment.htm>
More information about the Cs254f11
mailing list