During this activity, students should be able to:
This activity helps the student develop the following skills, values and attitudes: ability to analyze and synthesize, capacity for identifying and solving problems, and efficient use of computer systems.
Individually or in pairs, solve the following set of meta-programming exercises using Clojure macros.
Place all your functions in a namespace called macros
.
This is the documentation for Clojure's or
macro:
or
macro
Usage:
(or)
(or x)
(or x & next)
Evaluates its expressions one at a time, from left to right. If a form returns a logical true value, it returns that value and doesn't evaluate any of the other expressions, otherwise it returns the value of the last expression. (or) returns nil.
Write a macro called my-or
that works as described above. Make sure that any expression gets evaluated at most once. You may not use Clojure's or
macro. Usage examples:
(my-or) ⇨ nil (my-or false :one nil :two false :three) ⇨ :one (my-or false false nil) ⇨ nil (my-or nil nil false) ⇨ false
Write a macro called do-loop
that implements a post-test loop control statement. It must combine the functionality of C's do-while
statement and Pascal's repeat-until
statement.
This construct consists of a body (one or more expressions, presumably with side effects) and a final conditional form prefaced with a :while
or :until
keyword. First, the expressions in the body are evaluated sequentially, and then the condition is evaluated. If the final form uses a :while
keyword, the body of the loop is repeated while the condition holds true. On the other hand, if the final form uses an :until
keyword, the body of the loop is repeated while the condition holds false (or in other words, the loop terminates when the condition is true). Returns nil
.
Here are some examples:
(def i (atom 0)) (do-loop (println @i) (swap! i inc) (:until (= @i 5))) ;;; prints: ;;; 0 ;;; 1 ;;; 2 ;;; 3 ;;; 4 ⇨ nil (def j (atom 1)) (do-loop (println @j) (swap! j inc) (:while (<= @j 5))) ;;; prints: ;;; 1 ;;; 2 ;;; 3 ;;; 4 ;;; 5 ⇨ nil
Write a macro called def-pred
, that takes a name, an arg vector, and a body of one or more expressions. The macro should define two predicate functions: a regular one and its negated version. The name of the negated predicate should be the same as name but with a "not-
" prefix, and its result should be negated using the not
function (see the test code for an example).
Examples:
(macroexpand-1 '(def-pred less-than-one? [x] (< x 1))) ⇨ (do (clojure.core/defn less-than-one? [x] (< x 1)) (clojure.core/defn not-less-than-one? [x] (clojure.core/not (do (< x 1))))) (def-pred less-than-one? [x] (< x 1)) (less-than-one? 0) ⇨ true (less-than-one? 2) ⇨ false (not-less-than-one? 0) ⇨ false (not-less-than-one? 2) ⇨ true (macroexpand-1 '(def-pred plural? [s] (println "check s in" s) (= \s (last s)))) ⇨ (do (clojure.core/defn plural? [s] (println "check s in" s) (= \s (last s))) (clojure.core/defn not-plural? [s] (clojure.core/not (do (println "check s in" s) (= \s (last s)))))) (def-pred plural? [s] (println "check s in" s) (= \s (last s))) (plural? "boys") ;;; prints: ;;; check s in boys ⇨ true (plural? "girl") ;;; prints: ;;; check s in girl ⇨ false (not-plural? "boys") ;;; prints: ;;; check s in boys ⇨ false (not-plural? "girl") ;;; prints: ;;; check s in girl ⇨ true
NOTE: You will likely need to use the Clojure functions str
and symbol
when producing the negated predicate name.
Write a macro called defn-curry
, that performs a currying transformation to a function definition. It takes as parameters a name, an args vector, and a body of one or more expressions. The macro should define a function called name that takes only the first argument from args and returns a function that takes the second argument from args and returns a function that takes the third argument from args, and so on. The last function returned takes the last argument from args and evaluates all the expressions in body using a do
special form (see the test code for examples).
Usage examples:
(macroexpand-1 '(defn-curry sum [a b c d] (prn 'args a b c d) (+ a b c d))) ⇨ (clojure.core/defn sum [a] (clojure.core/fn [b] (clojure.core/fn [c] (clojure.core/fn [d] (do (prn (quote args) a b c d) (+ a b c d)))))) (defn-curry sum [a b c d] (prn 'args a b c d) (+ a b c d)) ((((sum 1) 2) 3) 4) ;;; prints: ;;; args 1 2 3 4 ⇨ 10 ((((sum 15) 8) 16) 42) ;;; prints: ;;; args 15 8 16 42 ⇨ 81 (macroexpand-1 '(defn-curry go [x y] (* x (+ y 1)))) ⇨ (clojure.core/defn go [x] (clojure.core/fn [y] (do (* x (+ y 1))))) (defn-curry go [x y] (* x (+ y 1))) ((go 2) 3) ⇨ 8 ((go 3) 2) ⇨ 9 (macroexpand-1 '(defn-curry add1 [x] (+ x 1))) ⇨ (clojure.core/defn add1 [x] (do (+ x 1))) (defn-curry add1 [x] (+ x 1)) (add1 0) ⇨ 1 (add1 41) ⇨ 42 (macroexpand-1 '(defn-curry hello [] "hello")) ⇨ (clojure.core/defn hello [] (do "hello")) (defn-curry hello [] "hello") (hello) ⇨ "hello"
Deliver a single file called macros.clj
containing your solutions. Please provide the following information:
IMPORTANT: The program source file must include at the top the author's personal information (name and student id) within comments. For example:
;;; ITESM CEM, April 14, 2016. ;;; Clojure Source File ;;; Activity: Macros ;;; Authors: ;;; A01166611 Pepper Pots ;;; A01160611 Anthony Stark . . (The rest of the program goes here) .
Due date is Thursday, April 14.
This activity will be evaluated using the following criteria:
-10 | The program doesn't contain within comments the author's personal information. |
---|---|
10 | The program contains syntax errors. |
DA | The program was plagiarized. |
10-100 | Depending on the amount of exercises that were solved correctly. |