Clojure: A Gentle Introduction (Chapter 7, Part 6)

Lisp’s every operator takes a predicate and a list as input. If there is no element inhibiting the predicate, then the answer is true. Some samples in Lisp:

The last one is especially interesting since Lisp’s every is able to operate on multiple lists, too. This is a bit different with Clojure. Here are Clojure’s appropriate expressions:

The first three expressions are very similar to their Lisp equivalents. In Clojure nil and the empty list / sequence are something different, that’s why (every? odd? …) and (every? even? …) yielding the same results is remarkable. Clojure’s every only allows two arguments, so we have to map the comparison in the last example before we are checking for the truth of all mappings. The result is the same.

The exercises 7.19 to 7.22 are asking for writing various functions:

  • ALL-ODD (exercise 7.19): return t / true if all elements in a list of numbers are odd,
  • NONE-ODD (exercise 7.20): return t / true if all elements in a list of numbers are not odd (that means: all have to be even),
  • NOT-ALL-ODD (exercise 7.21): return t / true if there is at least one even element in a list of numbers,
  • NOT-NONE-ODD (exercise 7.21): return t / true if there is at least one odd element in a list of numbers.

That’s the Lisp code:

And this is the same in Clojure:

We should take a look on not-all-odd in both dialects. find-if in Lisp returns the first even number (if found), while some in Clojure only returns true or false. Emulating find-if’s behaviour in Clojure is best done using the filter function (see part three) which does the same, but for all even numbers found by it. If we want just the first one, just use first.

Let’s skip the other exercises that run through redefining filters from A to Z and continue with something more interesting. Chapter 7’s keyboard exercise picks up a famous problem of Artificial Intelligence (I remember a Scientific American article from the early 1980s when I first read about it.) It’s ‘Block’s World’ and deals with various operations on a simple ‘world’ consisting of shapes of different size, material, and colours, and their appropriate position. I won’t repeat the various definitions of exercise 7.29; you can find them in David Touretzky’s Gentle Introduction to Common Lisp (which is freely available for download).

This is the Lisp code:

…and this is the same in Clojure:

There are some serious differences compared to the Lisp code. While the ‘database’ in the Lisp code is basically a list of lists, we have defined it as a map in the Clojure code. This results in a data structure that is much more a ‘database’ than a list of lists is: the keys are given by name, shape, colour, size, support and location information. This structure makes accessing each element very simple, as we can see in the code.

Both match-element functions are nearly identical. match-triple, however, deserves a bit more attention. Due to the changed structure of ‘database’ we have to regard the inequality of keys in the assertion- and pattern map. I.e., a call like (merge-with match-element '{ :block b1 :color green :shape brick } '{:block ? :color ?}) results in {:block true, :color true, :shape brick} – which would be false due to the unneeded ‘brick’ key. We solve this by limiting the comparison to only these key/value pairs that appear in both maps. We do this by calling select-keys with the pattern-map’s keys as a filter. Thus the call (select-keys (merge-with match-element '{ :block b1 :color green :shape brick } '{:block ? :color ?}) [:block :color]) takes only the pattern’s keys for comparison and results in {:color true, :block true} – which is exactly what we want.

fetch is very similar in both dialects despite the fact that we are using very different data structures in each language. While the Lisp fetch expects a call like (fetch '(b2 color ?)) the appropriate Clojure call is (fetch '{:block b2 :color ?}). And while exercise e just asks for a function that returns a block / colour pattern, the Clojure construct is a universal one, because the map structure makes it so remarkably easy. It just returns a map with the pattern as key (color, for example) and its corresponding attribute, e.g.

supporters returns a list of items supporting a given block, and again the map structure in the Clojure sample makes a different approach necessary. We are filtering for the given block (a possible call would be: (supporters 'b1)) and possible :supports or :supported-by keys in database. The output of this function is then used by supp-cube where we query the results of supporters for them being cubes. Basically here the same things are happening like in the Lisp code, but it’s places like this where you recognize the difference between Clojure and its ancestor.

Since we’ve been so hard-working, we are able to skip all following exercises, because they can be solved with simple fetch calls. This concludes chapter 7.

See part 1, part 2, part 3, part 4, part 5 of this series.

About Manfred Berndtgen

Manfred Berndtgen, maintainer of this site, is a part-time researcher with enough spare time for doing useless things and sharing them with the rest of the world. His main photographic subjects are made of plants or stones, and since he's learning Haskell everything seems functional to him.