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, 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:
(macros/my-or) ⇒ nil (macros/my-or false :one nil :two false :three) ⇒ :one (macros/my-or false false nil) ⇒ nil (macros/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)) (macros/do-loop (println @i) (swap! i inc) (:until (= @i 5))) ;;; prints: ;;; 0 ;;; 1 ;;; 2 ;;; 3 ;;; 4 ⇒ nil (def j (atom 1)) (macros/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 '(macros/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))))) (macros/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 '(macros/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)))))) (macros/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 '(macros/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)))))) (macros/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 '(macros/defn-curry go [x y] (* x (+ y 1)))) ⇒ (clojure.core/defn go [x] (clojure.core/fn [y] (do (* x (+ y 1))))) (macros/defn-curry go [x y] (* x (+ y 1))) ((go 2) 3) ⇒ 8 ((go 3) 2) ⇒ 9 (macroexpand-1 '(macros/defn-curry add1 [x] (+ x 1))) ⇒ (clojure.core/defn add1 [x] (do (+ x 1))) (macros/defn-curry add1 [x] (+ x 1)) (add1 0) ⇒ 1 (add1 41) ⇒ 42 (macroexpand-1 '(macros/defn-curry hello [] "hello")) ⇒ (clojure.core/defn hello [] (do "hello")) (macros/defn-curry hello [] "hello") (hello) ⇒ "hello"
Using the Online Assignment Delivery System (SETA), deliver the file called macros.clj
containing your solutions. No assignments will be accepted through e-mail or any other means.
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, November 26, 2013. ;;; Clojure Source File ;;; Activity: Macros ;;; Author: Steve Rogers, 1166611 . . (The rest of the program goes here) .
Due date is Tuesday, November 26.
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. |