Chapter 3. Lists, Vectors, and Higher-Order Functions

In this chapter, you will work with lists and vectors, along with the map, filter, and reduce functions. All of these take functions as one of their arguments, and are thus higher-order functions.

Étude 3-1: Move the Zeros

This is a quick warm-up étude. Given a list of integers that have zeros interspersed throughout, move all the zeros to the end. Name the function move-zeros; it accepts a list as an argument and returns a new list with the zeros at the end. I saw the problem at this page, solved in Java, and wondered if I could do it in ClojureScript. Answer: yes, I could. And so can you. Hint: filter is useful. After I solved it, I realized just how much my thinking about functional programming had changed the way I look at imperative code. You may have the same experience:

move-zeros.core=> (move-zeros [1 0 0 2 0 3 0 4 5 0 6])
(1 2 3 4 5 6 0 0 0 0 0)

See a suggested solution: “Solution 3-1”.

Étude 3-2: More List Manipulation

Write a function named ordinal-day that takes a day, month, and year as its three arguments and returns the ordinal (Julian) day of the year. Bonus points if you return zero for invalid dates such as 29-02-2015 or 40-40-2015. Don’t worry about handling dates before the year 1584 correctly.

You will need to know if a year is a leap year or not. I’ll give you that one for free:

(defn leap-year?
  "Return true if given year is a leap year; false otherwise"
  [year]
  (or (and (= 0 (rem year 4)) (not= 0 (rem year 100)))
    (= 0 (rem year 400))))

Some sample output from the REPL:

formulas.core=> (ordinal-day 1 3 2015)
60
formulas.core=> (ordinal-day 1 3 2016)
61
formulas.core=> (ordinal-day 1 13 2015)
0
formulas.core=> (ordinal-day 29 2 2015)
0
formulas.core=> (ordinal-day 29 2 2016)
60
formulas.core=> (ordinal-day 31 9 2015)
0

Then, modify the daylight calculator from Chapter 2 to allow entry of a date in the form yyyy-mm-dd. You will need to split the input data into individual numbers. You can use either the split method for JavaScript strings or the split method from the clojure.string library. If you want to use the latter method, you will need to add that library to your require:

(ns stats.core
  (:require [clojure.browser.repl :as repl]
            [clojure.string :as str]))

To specify a regular expression for split, prefix a string with #. Here is some sample output from the REPL. Using JavaScript’s split returns a JavaScript array. Notice that you do not need to escape backslashes in patterns (see the last example):

formulas.core=> (require '[clojure.string :as str])
nil
formulas.core=> (.split "a:b:c:d" #":")
#js ["a" "b" "c" "d"]
formulas.core=> (str/split "a:b:c:d" #":")
["a" "b" "c" "d"]
formulas.core=> (str/split "abc123def456ghi789jkl" #"\d+")
["abc" "def" "ghi" "jkl"]
formulas.core=>

Bonus points: display the daylight as hours and minutes. Here is the relevant HTML to put in your index.html file:

<h1>Amount of Daylight</h1>
<p>
Latitude: <input type="text" size="8" id="latitude" />°<br />
Enter date in format <em>yyyy-mm-dd</em>: 
<input type="text" size="15" id="gregorian" /><br />
<input type="button" value="Calculate" id="calculate"/>
</p>

<p>
Amount of daylight: <span id="result"></span>
</p>

See a suggested solution: “Solution 3-2”.

Étude 3-3: Basic Statistics

Create a project named stats and write these functions, each of which takes a list of numbers as its argument:

mean
Calculates the arithmetic average of the list by summing the numbers (hint: use reduce or apply +) and dividing by the number of items in the list.
median
Calculates the median of the numbers. The algorithm is as follows:
  1. Sort the list (hint: use sort).
  2. If n, the number of items in the list, is odd, take the item at position (n − 1) / 2.
  3. Otherwise, take the average of the items at positions n / 2 and (n − 1) / 2.

I used drop in my solution rather than nth.

stdev
Calculates the standard deviation of the numbers. Use the computational formula: , which works out to this algorithm:
  1. Get the sum of the squares of all the numbers in the list.
  2. Get the sum of all the numbers of the list, and square that result.
  3. Divide the result of step 2 by n.
  4. Subtract the result of step 3 from the result of step 1.
  5. Divide the result of step 4 by n—1.
  6. Take the square root of the result of step 5.

You could write two functions that use reduce: one to get the sum of the list and another to get the sum of squares, but, as a challenge, try to write a single function to get both numbers. Hint: there is no law that says the “accumulator” of the function that you give to reduce has to be a single number. It could just as well be a vector of two items. If you take this approach, you might want to make the reducing function a separate function rather than an anonymous function.

See a suggested solution: “Solution 3-3”.

Étude 3-4: Basic Statistics in a Web Page

Now that you have the functions working, connect them to a web page where people can enter a list of numbers and the program will display the resulting statistics when the input field changes. Here’s the HTML:

<!DOCTYPE html>
<html>
    <head>
        <title>Basic Statistics</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    <body>
        <h1>Basic Statistics</h1>
        <p>
        Enter numbers, separated by blanks or commas:
        <input type="text" size="50" id="numbers"/>
        </p>

        <p>
        Mean: <span id="mean"></span><br />
        Median: <span id="median"></span><br />
        Standard deviation: <span id="stdev"></span>
        </p>

        <script src="out/stats.js" type="text/javascript"></script>
    </body>
</html>

Once you have the individual items, you have to use js/window.parseFloat to convert them to numbers. You must do this because ClojureScript’s (and JavaScript’s) + operator works differently on strings than on numbers: (+ "12" "30") works out to "1230", not 42. Hint: use map.

Use whichever method of interacting with JavaScript (see Chapter 2) that you prefer. In this étude, you will listen for a change event, and you may want to use the JavaScript event.target property. Given a function like (defn handler [evt] ...), here is how you access the value of a form field via the target property:

LibraryClojureScript
JavaScript
Google Closure
(.-value (.-target evt))
dommy(dommy/value (.-target evt))
Domina(domina/value (domina.events/target evt))
Enfocus(ef/at (.-target evt) (ef/get-prop :value))

See a suggested solution: “Solution 3-4”.

Étude 3-5: Dental Hygiene

OK, I’ll admit this is a fairly strange étude, but I couldn’t resist. Dentists check the health of your gums by checking the depth of the “pockets” at six different locations around each of your 32 teeth. The depth is measured in millimeters. If any of the depths is greater than or equal to four millimeters, that tooth needs attention. (Thanks to Dr. Patricia Lee, DDS, for explaining this to me.)

Your task is to write a function named alert that takes a vector of 32 vectors of six numbers as its input. If a tooth isn’t present, it is represented by the empty vector [] instead of the six numbers. The function produces a list of the tooth numbers that require attention. The numbers must be in ascending order.

Here’s a definition of a set of pocket depths for a person who has had her upper wisdom teeth, numbers 1 and 16, removed. Just copy and paste it into your project. Note that list entries may be separated by either a comma or by spaces:

(def pocket-depths
  [[], [2 2 1 2 2 1], [3 1 2 3 2 3],
  [3 1 3 2 1 2], [3 2 3 2 2 1], [2 3 1 2 1 1],
  [3 1 3 2 3 2], [3 3 2 1 3 1], [4 3 3 2 3 3],
  [3 1 1 3 2 2], [4 3 4 3 2 3], [2 3 1 3 2 2],
  [1 2 1 1 3 2], [1 2 2 3 2 3], [1 3 2 1 3 3], [],
  [3 2 3 1 1 2], [2 2 1 1 3 2], [2 1 1 1 1 2],
  [3 3 2 1 1 3], [3 1 3 2 3 2], [3 3 1 2 3 3],
  [1 2 2 3 3 3], [2 2 3 2 3 3], [2 2 2 4 3 4],
  [3 4 3 3 3 4], [1 1 2 3 1 2], [2 2 3 2 1 3],
  [3 4 2 4 4 3], [3 3 2 1 2 3], [2 2 2 2 3 3],
  [3 2 3 2 3 2]])

And here’s the output:

cljs.user=> (in-ns 'teeth.core)
nil
teeth.core=> (alert pocket-depths)
[9 11 25 26 29]
teeth.core=>

See a suggested solution: “Solution 3-5”.

Étude 3-6: Random Numbers—Generating a Vector of Vectors

How do you think I got the numbers for the teeth in the preceding étude? Do you really think I made up and typed all 180 of them? No, of course not. Instead, I wrote a ClojureScript program to create the vector of vectors for me, and that’s what you’ll do in this étude.

ClojureScript is luckily provided with the rand function. It generates a random floating-point number from 0 up to but not including 1 (if given no argument); or, if given a single argument n, returns a random floating value from 0 up to n. More useful for this étude is the rand-int function, which takes one argument n and returns a random integer from 0 up to but not including n.

Create a project named make_teeth and write a function generate-pockets that takes two arguments. The first argument is a string consisting of the letters T and F. A T indicates that the tooth is present, and an F indicates a missing tooth. The second argument is a floating-point number between 0 and 1.0 (inclusive) that indicates the probability that a tooth will be a good tooth.

The result is a vector of vectors, one subvector per tooth. If a tooth is present, the subvector has six entries; if a tooth is absent, the sublist is empty: []. Here is some sample output from the REPL:

make_teeth.core=> (generate-pockets "TFTT" 0.75)
[[1 2 2 3 1 1] [] [2 3 1 1 3 2] [4 2 2 3 2 3]]

These are the helper functions I needed:

(generate-list teeth-present probability result)
The first two arguments are the same as for generate_pockets; the third argument is the accumulated list. If a tooth isn’t present, add [] to the result; otherwise, add the return value of generate_tooth with the probability of a good tooth as its argument.
(one-tooth present probability)
This function takes as its arguments a single-character string ("T" or "F") to signiify the presence or absence of a tooth and the probability of a good tooth. If there’s no tooth, it returns []. Otherwise, it sets a “base depth” for all the pockets by generating a random number between 0 and 1. If that number is less than the probability of a good tooth, base depth is 2; otherwise, it’s 3. It then generates a vector of six numbers, each time randomly adding an integer from -1 to 1 to the base depth.

I imagine that, with a great deal of effort, I could have found a way to use map and reduce to give me the results I wanted, but I was too lazy. Instead, I used recur in generate-list and loop/recur in one-tooth.

See a suggested solution: “Solution 3-6”.

Étude 3-7: Monthly Daylight

This étude puts together a lot of the things you have been doing in this chapter into one rather large-ish project. The project name is daylight_summary, and it gives a table of average minutes of daylight per month for a given latitude or city (selected from a drop-down menu). Here is the HTML:

<!DOCTYPE html>
<html>
    <head>
        <title>Amount of Daylight</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style type="text/css">
          th, td {
            border: 1px solid gray;
            padding: 0.5em;
          }
        </style>
    </head>
    <body>
        <h1>Amount of Daylight</h1>
        <p>
        <input type="radio" name="locationType" id="menu" checked="checked">
          <select id="cityMenu">
            <option value="39.9075">Beijing</option>
            <option value="52.52437">Berlin</option>
            <option value="-15.77972">Brasília</option>
            <option value="30.06263">Cairo</option>
            <option value="-35.28346">Canberra</option>
            <option value="-17.82772">Harare</option>
            <option value="-12.04318">Lima</option>
            <option value="51.50853">London</option>
            <option value="55.75222">Moscow</option>
            <option value="-1.28333">Nairobi</option>
            <option value="28.63576">New Delhi</option>
            <option value="12.36566">Ouagadougou</option>
            <option value="59.91273">Oslo</option>
            <option value="48.85341">Paris</option>
            <option value="35.6895">Tokyo</option>
            <option value="38.89511">Washington, D. C.</option>
          </select>
          <input type="radio" id="userSpecified" name="locationType">
          Other latitude: <input type="text" size="8" id="latitude"/>
          <input type="button" value="Calculate" id="calculate"/>
        </p>
        
        <h2>Monthly Average Daylight</h2>
        <table>
          <thead><tr><th>Month</th><th>Average</th></tr></thead>
          <tbody>
            <tr><td>January</td><td id="m1"></td></tr>
            <tr><td>February</td><td id="m2"></td></tr>
            <tr><td>March</td><td id="m3"></td></tr>
            <tr><td>April</td><td id="m4"></td></tr>
            <tr><td>May</td><td id="m5"></td></tr>
            <tr><td>June</td><td id="m6"></td></tr>
            <tr><td>July</td><td id="m7"></td></tr>
            <tr><td>August</td><td id="m8"></td></tr>
            <tr><td>September</td><td id="m9"></td></tr>
            <tr><td>October</td><td id="m10"></td></tr>
            <tr><td>November</td><td id="m11"></td></tr>
            <tr><td>December</td><td id="m12"></td></tr>
           
          </tbody>
        </table>
        <script src="out/daylight_summary.js" type="text/javascript"></script>
    </body>
</html>

In this program, don’t worry about leap years; do the calculation based on a 365-day year. To determine which of the radio buttons is selected, you use code like this in Enfocus, where ef is the abbreviation for the enfocus.core namespace:

(ef/from "input[name='locationType']" (ef/get-prop :checked)))

The selector is a CSS style selector, and the expression returns a list of the status of the two radio buttons, with true if selected and false if not.

If you are using Domina, use code like this, again using a CSS selector:

(def radio (domina/nodes (domina.css/sel "input[name='locationType']")))
(domina/value (first radio))

The result of the second expression is the string "on" if the radio button is selected, nil if not.

See a suggested solution: “Solution 3-7”.