CIS 554 Clojure 1: Exercises
Fall 2016, David Matuszek

Purposes of this assignment

Details

Write and test the following functions. Please be sure to get the spelling and capitalization right, and the right number and types of parameters, in order to make testing them feasible.

These are mostly unrelated exercises, to give you some experience with Clojure syntax. The exercises in the first group do not require higher-order functions, the exercises in the second group might be done with higher-order functions, and the exercises in the third group are higher-order functions. Almost all are recursive.

In some cases, for example reversing a list, you may find Clojure functions that already do exactly what you want. (This is certainly true for the functions whose names begin with my-.) Please don't use them, but rather write the functions yourself.

Most of these functions are defined to work with lists, because my experience in Lisp has been almost entirely with lists. If your functions works with lists, they will probably also work with other kinds of sequences.

In all cases, assume that the inputs to the function are correct; don't do any error checking.

First group

You don't need higher-order functions to program these, but if you see an opportunity to use them, go ahead. Generally, the purpose is to get you used to writing recursive functions the Clojure way.

(atomic? v)
Returns true if v is not any kind of a collection (check with coll?), and false otherwise.
(member? x lst)
Returns a true value if x is in lst.
(my-count lst)
Returns the number of "top level" elements in lst. For example, (a (b c (d e)) f) has 3 top level elements; (b c (d e)) is a single top-level element. (a b () d) has 4 top-level elements.
(append lst1 lst2)
Combine lst1 and lst2 into a single list. For example, if lst1 is (:a :b :c) and lst2 is (1 (2 3)), the result should be (:a :b :c 1 (2 3)).
(zip lst1 lst2)
Combine corresponding elements of lst1 and lst2 into a list of two-element lists; stop when you run out of elements in either list. For example, if lst1 is (:a :b :c) and lst2 is (1 (2 3) 4 5), the result should be ((:a 1) (:b (2 3)) (:c 4)).
(lookup key list-of-pairs)
Given an S-expression key and a list of (key value) pairs, return the value that corresponds to the key, or nil if there is no such pair. You can assume that there is only one such pair.
(my-merge lst1 lst2)
Given two lists of integers, where each list is in ascending order, merge them into a single list that is also in ascending order. For example, if lst1 is (3 7 12 19 19 25 30) and lst2 is (4 7 10 12 20), the result should be (3 4 7 7 10 12 12 19 19 20 25 30).
(count-all lst)
Returns the total number of atomic elements in lst, at all levels. For example, (a (b c () (25) nil) ()) has 5 atomic elements: a, b, c, 25, and nil.
(my-drop n lst)
Returns the lst with the first n elements removed. For example, (my-drop 3 '(a b c d e)) should return (d e)). If n is equal to or greater than the length of the list lst, return the empty list, ().
(my-take n lst)
Returns a list of the first n elements of lst. If lst has fewer than n elements, the result is just lst. Strong hint: Use two parameters lists, the second one using an "accumulator" parameter to collect the elements taken from lst. You are likely to find reverse to be useful.
(my-reverse lst)
Reverses the elements of list lst. For example, the list (1 2 (3 4)) becomes the sequence ((3 4) 2 1).
(remove-duplicates lst)
Removes duplicate top-level elements of lst. For example, given (1 2 3 1 4 1 2), remove-duplicates returns a sequence containing the elements (1 2 3 4), in some order.
(my-flatten list-of-lists)
Removes one level of parentheses (or brackets) removed, returning a "flatter" list of values. For example, if lst is (((1 1) (2 3)) ((5 7))), the result should be ((1 1) (2 3) (5 7)).

Second group

These are best done with higher-order functions, so please use them wherever they seem to work.

(buzz list-of-ints)
Return the same list of integers as given as an argument, except that every number divisible by 7, or containing the digit 7, has been replaced by :buzz. Hint: (seq string) returns a list of characters, and map is useful.
(divisors-of n)
For positive integer n, returns the divisors of n, other than 1 and n itself. For example, 12 has divisors (2, 3, 4, 6). Hint: Use mod and filter.
(longest list-of-strings)
Returns the longest string in the list-of-strings; if there is more than one longest string, return the earlier one. Assume that list-of-strings is not empty. Hint: reduce.

Third group

These are your versions of the most commonly used higher-order functions.
(my-map f lst)
Apply the function f to each element of lst, returning a list of the results.
(my-filter pred lst)
Apply the test pred to each element of lst, returning a list of the ones that pass the test.
(my-reduce f value? lst)
Applies the two-parameter function f to the value? and the first element of the sequence (if value? is present), else to the first two elements of the sequence; applies the function to the result and the next element in the list, recursively. Hint: Since this function takes two or three arguments, you have to use the proper syntax for this. See Defining and calling functions in my Concise Guide to Clojure.
(my-flat-map f lst)
f must be a function that returns lists. my-flat-map applies the function f to each element of lst, flattens the resultant list by removing one level of parentheses, and returns the result.

Unit testing

Write, in a separate file, unit tests for all your methods. Since we haven't talked about testing yet, here's a model for you to follow. Notice the use of deftest and is.

(ns user (:use clojure.test))

(deftest test-my-reverse
  (is (= '(3 2 1) (my-reverse '(1 2 3))))
  (is (= '(5 (3 4) 2 1) (my-reverse '(1 2 (3 4) 5))))
  (is (= () (my-reverse ()))) )

(run-tests)

Due date:

Zip and turn in your exercises.clj and exercises-test.clj files by 6am, Tuesday, September 27 As always, only files submitted to Canvas will be accepted. I do not accept assignments submitted by email.