Manipulating Collections - filter and partial

The filter function processes a collection and yields a sequence of the items that fit certain criteria.

Here is the general form of the filter function that we will use:

(reduce predicate collection)

where

filter applies the predicate function to each item in the collection. If the function returns true, the item goes into the output sequence, otherwise it doesn’t.

As an example, here is filter used to extract only numbers that are multiples of four:

Note

By convention, boolean functions (functions that return true or false) have names ending with a question mark. Some of the built-in predicates in ClojureScript are even?, odd?, pos? (positive), neg? (negative), zero?, and empty? (tests if a collection is empty or not).

Functions that Create Functions: partial

Now, let’s say you wanted to write a filter for a set of scores that keeps only the scores that are above average. You have to go through the scores twice: once to find the average, and then to filter the data that you want. (In computer science, this is called a “two-pass algorithm.”) You would like to write something like this:

(def scores [81 72 95 84 91 77])

(defn average [coll]
    (let [n (count coll)
          total (reduce + coll)]
        (if (> n 0) (/ total n) 0)))

; this filter function will not work
(defn above-average? [avg value]
    (> value avg))

(let [avg (average scores)]
    (filter above-average? scores))

There’s only one problem with this: the predicate function to filter takes only a single argument, and you need two pieces of information: the current score and the average. You can’t write the above-average? function like this:

(defn above-average? [value]
    (> value avg))

because you don’t know the average in advance. You could use def to bind avg to some value and then change it later with set!, but that goes counter to the principles of functional programming—you don’t want to re-bind a symbol to a new value if you don’t have to, and in this case, you don’t have to.

Instead, you can use partial. Before showing you how it is written, here’s the idea behind it. When you log in to an e-commerce site you’ve used before to purchase a new item, the checkout form shows up with much of the information already filled in (name, shipping/billing address). All you need to do is complete one or two items. In essence, the form is partially filled in.

So here’s the trick: we’re going to give partial the two-parameter function and the first argument. The result will be a partially-called function that is waiting to be called with the second argument, much as the web site sends you a partially-completed form that is waiting to be completed with extra information.

Before doing the whole program, let’s see how you could write a partial function for filtering averages above 50. I’ve put the symbols in the expression in above-average in the same order as the parameters.

A picture of what’s happening here might be useful:

Function box diagram showing above-average? and 50 as inputs to partial, with a new function as its output

Essentially, partial has constructed a one-parameter function with the 50 “baked in” as the first argument. Thus, here is the solution for filtering only those scores greater than the average:

The key here is above-calculated-average? (partial above-average? avg). As soon as you do know the average of the scores, you use partial to provide a new function of one argument that returns true for values above the avg, which is exactly what filter requires.

Exercise Remember a few pages back where you used map to calculate a 10% discount on a vector of prices? This time, you are going to write a program that uses map to calculate a discount that’s passed as an argument. Complete this code; you will use partial to get the job done. The multi-line quoted string between discount and its arguments is a conventional way to comment a function in ClojureScript.

Next Section - Threading Function Calls