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 interspered 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 Interacting With JavaScript and Web Pages to allow entry of a date in the form yyyy-mm-dd. You will need to split the input data into individual numbers. You could use the split method for JavaScript strings, or you can use 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]
            <strong>[clojure.string :as str]</strong>))

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).

formula.core=> (require '[clojure.string :as str])
nil
formula.core=> (.split "a:b:c:d" #":")
#js ["a" "b" "c" "d"]
formula.core=> (str/split "a:b:c:d" #":")
["a" "b" "c" "d"]
formula.core=> (str/split "abc123def456ghi789jkl" #"\d+")
["abc" "def" "ghi" "jkl"]
formula.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:

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 Interacting With JavaScript and Web Pages) 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 sub-vector per tooth. If a tooth is present, the sub-vector 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