Sometimes I end up with layers of if statements in my lisp code. Is there any alternative to doing this?
It's one of those cases where the answer, unfortunately, is "it depends". In some cases, the obvious choice is to use cond.
(if condition1
result1
(if condition2
result2
default))
;; naturally converted to:
(cond (condition1 result1)
(condition2 result2)
(t default))
At other times, cond paired with a bit of or or and may be exactly what you want.
(if condition1
(if condition2
result12
result1)
(if condition2
result2
default))
;; naturally turns intoteh answe
(cond ((and condition1 condition2) result12)
(condition1 result1)
(condition2 result2)
(t default))
NB, I have only written, not tested, this code, but in principle it should be fine.
Unfortunately, there are probably times where even the somewhat simpler-to-read cond form isn't clear enough and in those cases it's usually worth staring at the actual problem a bit harder. There may be a way of decomposing the logic in a better way (dispatch table, outer logic calling functions with suitable parameters, ...).
There are few directions you can move in to change your code.
Creating higher order functions that encapsulate conditions, or, perhaps, macros, or, maybe reusing existing macros (take a look at case macro for example). remove-if would be an example of a high-order function that can spare you writing an "if".
Use polymorphism. Lisp has classes, objects, overloads and all the instrumentation you would expect an object system to have (perhaps even a little extra ;).
Suppose you have this code:
;; Suppose your `person' is a list that has that person's
;; name as its first element:
(defun hello! (person)
(if (string= (car person) "John")
(concatenate 'string "Hello, " (car person))
(print "Hello, who are you?")))
(defun goodbye! (person)
(if (string= (car person) "John")
(concatenate 'string "Goodbye, " (car person))
(print "Goodbye, mysterious stranger!")))
;; You would use it like so:
(hello! '("John" x y z))
(goodbye! '(nil x y z))
Now, you could rewrite it as:
(defclass person () ())
(defclass friend (person)
((name :accessor name-of
:initarg :name)))
(defgeneric hello! (person))
(defgeneric goodbye! (person))
(defmethod hello! ((person person))
(print "Hello, who are you?"))
(defmethod hello! ((person friend))
(print (concatenate 'string "Hello, " (name-of person))))
(defmethod goodbye! ((person person))
(print "Goodbye, mysterious stranger!"))
(defmethod goodbye! ((person friend))
(print (concatenate 'string "Goodbye, " (name-of person))))
;; Which you would use like so:
(hello! (make-instance 'person))
(goodbye! (make-instance 'friend :name "John"))
;; Note that however the later is significantly more verbose
;; the ratio will change when you have more `if's which you
;; can systematize as objects, or states.
In other words, by encapsulating the state you can avoid explicitly writing condition statements. You would also gain some readability on the way, as you wouldn't need to memorize which piece of the list contains what info - you would now have it labelled "name".
Related
I am using Emacs, Slime, and SBCL. Simplifying a problem that I am facing, suppose I have this function working:
(defun get-answer (x y z)
(format t "Which animal would you like to be: ~s ~s ~s ~%" x y z)
(let ((answer (read-line)))
(cond ((equal answer x) (format t "user picks ~s" x))
((equal answer y) (format t "user picks ~s" y))
((equal answer z) (format t "user picks ~s" z)))))
It works for a fixed number of inputs:
CL-USER> (get-answer "fish" "cat" "owl")
Which animal would you like to be: "fish" "cat" "owl"
fish
user picks "fish"
NIL
I would like to generalize this function writing a variant argument macro that builds a variant number of cond clauses. Basically, Common Lisp macro would write the cond clause for me :)
For instance, I wish I could call it like:
CL-USER> (get-answer '("pig" "zebra" "lion" "dog" "cat" "shark"))
Or just:
CL-USER> (get-answer '("dog" "cat"))
Either way, it would generate 6 and 2 appropriate cond clauses, respectively. I tried building something to achieve this goal.
My draft/sketch focusing on the cond clause part is:
(defmacro macro-get-answer (args)
`(cond
(,#(mapcar (lambda (str+body)
(let ((str (first str+body))
(body (second str+body)))
`((string= answer ,str)
,body)))
args))))
The variable answer was supposed to hold the value inserted by the user. However, I can't manage to make it work. slime-macroexpand-1 is not being really helpful.
As an error message, I get:
The value
QUOTE
is not of type
LIST
Which is not something that I was expecting. Is there a way to fix this?
Thanks.
I don't think you need a macro. Note that there's a repeating pattern of ((equal answer x) (format t "user picks ~s" x)), so you should think how to simplify that.
So, this is your function and expected inputs:
(defun get-answer (x y z)
(format t "Which animal would you like to be: ~s ~s ~s ~%" x y z)
(let ((answer (read-line)))
(cond ((equal answer x) (format t "user picks ~s" x))
((equal answer y) (format t "user picks ~s" y))
((equal answer z) (format t "user picks ~s" z)))))
(get-answer '("pig" "zebra" "lion" "dog" "cat" "shark"))
(get-answer '("dog" "cat"))
I'd write something like this:
(defun get-answer (args)
(format t "Which animal would you like to be: ~{~s ~} ~%" args)
(let ((answer (read-line)))
(when (member answer args :test #'string=)
(format t "User picks ~s" answer)
answer)))
Tests:
(get-answer '("pig" "zebra" "lion" "dog" "cat" "shark"))
(get-answer '("dog" "cat"))
Note that your function always returned NIL, mine returns chosen string or NIL. After you recieve user input, you probably don't want to discard it immediately.
And if you have string with values, like this:
(get-answer '(("pig" 1) ("zebra" 3) ("lion" 2) ("dog" 8) ("cat" 12) ("shark" 20)))
I'd use find:
(defun get-answer (args)
(format t "Which animal would you like to be: ~{~s ~} ~%" (mapcar #'first args))
(let* ((answer (read-line))
(match (find answer args :test #'string= :key #'car)))
(when match
(format t "User picks ~s " (first match))
(second match))))
This function returns NIL or value of chosen animal.
The question is a little bit strange, but maybe the point is just to learn about Common Lisp's macros. One could just calculate the members of the condition in a first place. That means, generate a list of string= forms. Then just return a list containing the cond special form and the list of ((string= str) return-value).
(defmacro get-answer (answers)
(let ((clauses (mapcar (lambda (str+body)
(let ((str (first str+body))
(body (second str+body)))
`((string= ,str) ,body))) answers)))
`(cond ,#clauses)))
To test macros, one can use macroexpand and macroexpand-1.
> (macroexpand-1 '(get-answer (("test1" 1) ("test2" 2))))
(COND
((STRING= "test1") 1)
((STRING= "test2") 2))
But I don't personally think it is worth a macro. I think there is nothing wrong with the following function:
(defun get-answer (answer)
(cond
((string= answer "dog") 6)
((string= answer "cat") 2)))
As with Martin Půda's answer: this is not a good place where you need a macro. In particular if you want some undetermined number of options, pass a list of them: Common Lisp has extremely good list-handling functions.
In the simple case you give, the function becomes almost trivial:
(defun get-answer (options)
(format t "Which animal would you like to be (from ~{~A~^, ~})? " options)
(let ((selected (car (member (read-line) options :test #'string-equal))))
(format t "picked ~S~%" (or selected "(a bad answer)"))
selected))
If what you want to to is to map a value selected by the user to another value, then what you want is an alist or a plist:
(defun get-answer/map (option-map)
(format t "Which animal would you like to be (from ~{~A~^, ~})? "
(mapcar #'car option-map))
(let ((selected (assoc (read-line) option-map :test #'string-equal)))
(format t "picked ~A~%" (if selected (car selected) "(a bad value)"))
;; Second value tells us if fn succeeded, as first can be NIL
(values (cdr selected) selected)))
The whole purpose of functions like member and assoc is to search for entries in lists: there is no use in laboriously reimplementing half-working versions of them by writing lots of cond clauses, whether or not you do this automatically.
Now, people often say 'but I want to learn macros, so I will start by learning how to rewrite functions as macros'. That is a not where to start: macros are never useful for rewriting functions in this way, and thinking they are is very damaging.
Here's one simple example of a case where this can't work:
(get-answer (read-options-from-file *my-options-file*))
If get-answer were a macro how is this meant to work? The number of options is not only unknown at macroexpansion time, it is not even knowable, even in principle, because neither the file's name nor its contents can possibly be known at compile time.
Macros are not replacements for functions: they are are functions between languages: a macro compiles a language which is CL (+ other macros) + the language understood by the macro into a language which is CL (+ other macros).
I am slightly struggling to think of a case where a macro might be useful here, but perhaps we can imagine a language which has a with-answer-options form:
(with-answer-options (a)
("elephant" (explode-world :because-of a))
("fish" (set-fire-to-computer :blaming a)))
Note that this form now looks nothing like a function call: it is instead a little language which slightly extends CL, and which will get mapped to CL by the following macro:
(defmacro with-answer-options ((var &optional (prompt "Pick an answer"))
&body clauses)
(unless (symbolp var)
(error "~S should be a variable" var))
(let ((choices (mapcar #'car clauses)))
(unless (every #'stringp choices)
(error "choices should be (<literal string> form ...)"))
`(let ((,var (progn (format t "~A from ~{~A~^ ~}? "
,prompt ',choices)
(read-line))))
(cond
,#(mapcar (lambda (clause)
(destructuring-bind (val &body body) clause
`((string-equal ,var ',val)
,#body)))
clauses)
(t (error "~A is not one of ~{~A~^ ~}" ,var ',choices))))))
So now:
> (with-answer-options (a "Pick a bad thing")
("elephant" (explode-world :because-of a))
("fish" (set-fire-to-computer :blaming a)))
Pick a bad thing from elephant fish? frog
Error: frog is not one of elephant fish
There is a QUOTE, because your macro form contains one:
CL-USER 8 > (defmacro macro-get-answer (args) (print args) nil)
MACRO-GET-ANSWER
CL-USER 9 > (macro-get-answer '(1 2 3))
(QUOTE (1 2 3))
NIL
CL-USER 10 > (macro-get-answer (quote (1 2 3)))
(QUOTE (1 2 3))
NIL
Possible solutions:
don't use a quote
remove the quote
How can one translate the following code
while ((readInteger = fileInputStream.read()) != -1) {
.....
}
in clojure ? I need the value of readInteger in further parts of the code but also the '!= -1' needs to take place inside the while conditional.
some general patterns for adapting things to the Clojure syntax
move the ( to the left of the function or opperator.
move opperators to the left of the things they work on and surround with ( )
so you could start like this:
(while (not= (.read fileInputStream) -1 ... and so on.
then, since you need to use the readInteger value later in the code let's talk about naming values and looping. If you just wanted to read a value once and give it a name you could do it like this:
(let [readInteger (.read fileInputStream)]
... your code here)
Since you want to do it in a loop, then let's use loop instead of let:
(loop [readInteger (.read fileInputStream)]
... your code here
(if (not= readInteger -1)
(recur (.read fileInputStream))))
or for (which is not the "for loop" from other languages)
(for [readInteger (repeatedly #(.read fileInputStream))
:while (not= readInteger -1)]
... do somethign with readInteger ...)
For generates sequences of results rather than just looping like it does in other languages.
Then the next step in clojuring is to think about how to split the reading the data from processing it. We can:
make a sequence of all the data
process each data
something like this:
(let [data (line-seq fileInputStream)]
(map #(Integer/parseInt %) data)
...)
There are functions in the standard library for converting a great many things into sequences, and a bunch of functions for doing a great many things with sequences.
Don't solve this problem with while, which requires you to do your test at the beginning of the loop. Instead, think about a recursive function, which can decide at any part of its body whether to make the recursive call or not. Any iterative loop can be converted into a tail-recursive function using loop/recur; here's an example of how to do it with your loop.
(loop []
(let [read-integer (.read file-input-stream)]
(when (not= read-integer -1)
(...)
(recur))))
Here are two similar examples like amalloy suggested:
(ns xyz...
(:require [clojure.java.io :as io] )
(:import [java.io StringReader] ))
(newline) (newline)
(let [reader-2 (io/reader (StringReader. "first")) ]
(loop []
(let [curr-char-int (.read reader-2)]
(when (not= -1 curr-char-int)
(print (char curr-char-int) " ")
(recur)))))
(newline) (newline)
(let [reader-2 (io/reader (StringReader. "second")) ]
(loop [curr-char-int (.read reader-2)]
(when (not= -1 curr-char-int)
(print (char curr-char-int) " ")
(recur (.read reader-2)))))
With result:
> lein run
f i r s t
s e c o n d
In the first case it takes an extra let statement, but doesn't duplicate the part (.read reader-2) like the 2nd case does.
Using threading macro:
(->> (repeatedly #(.read fs))
(take-while (partial not= -1))
(map str))
Replace (map str) with whatever function you want to operate
on the stream. For example, to calculate the sum:
(->> (repeatedly #(.read fs))
(take-while (partial not= -1))
(reduce +))
I want to write a function (rep-n-times n & args), which should work like:
user=>(rep-n-times 3 (print "hello!") (print "bye"))
hello! bye hello! bye hello! bye nil
My code is
(defmacro ntimes [n & body]
`(take ~n (repeat ~#body)))
Testing:
#'user/rep-n-times
user=> (rep-n-ntimes 5 (print "hah"))
hah(nil nil nil nil nil)
user=> (macroexpand '(rep-n-ntimes 4 (print "hello")))
(clojure.core/take 4 (clojure.core/repeat (print "hello")))
How can I fix it?
In this case where you are doing things for side effects, you should use doseq or dotimes instead:
(dotimes [i 3]
(print "hello! bye "))
There is no need to define rep-n-times. If you need the results of a function with side-effects, use repeatedly. Note also that repeatedly and repeat optionally takes the number of repetitions as an argument.
(repeatedly 3 (fn [] (print "hello! bye ") (rand-int 10)))
However as to the problem with your definition of rep-n-times, calling repeat takes a single argument, which is the evaluated result of (print "hello") which is nil. So you are asking for 4 nils, and getting 4 nils. The print occurs one time, when the argument is evaluated to nil. Also it produces a lazy sequence, which happens to be evaluated at the REPL, but that is just because it is being printed. You should avoid having side effects (such as printing) inside a lazy sequence, because they will not be evaluated unless you explicitly realize the sequence.
Note that dotimes can take many forms:
(dotimes [i 3] (print "h1") (print "h2") (print "h3"))
And that dotimes is a macro defined here
You can write your own version by using that code as a starting point:
(defmacro rep-n-times [n & body]
`(loop [i# ~n]
(when (pos? i#)
~#body
(recur (dec i#)))))
Say I have the following code:
(defmacro test1 [x]
(list 'fn '[y]
(if (pos? x)
'(println y)
'(println (- y)))))
It does what I need, composes a function based on x, and leaves no references to x. For example, (test1 1) macroexpands into (fn* ([y] (println y))).
Now, I'd like to rewrite it using syntax quoting. This is what I have so far:
(defmacro test2 [x]
`(fn [y#]
(if ~(pos? x)
(println y#)
(println (- y#)))))
This does exactly the same, with one exception: it leaves an (if true ..) expression in the expanded expression:
(fn* ([y__12353__auto__]
(if true
(clojure.core/println y__12353__auto__)
(clojure.core/println (clojure.core/- y__12353__auto__)))))
This might not be an issue if the compiler can optimize it out. Still, is there a way I could omit it?
When you use test2 it will unquote the whole form (pos? x) which will work at compile time if it's a constant number or perhaps a gloabl that is already defined, but not if you pass a lexically scoped variable name that doesn't exist yet.
Thus, you really want this instead:
(defmacro test2 [x]
`(fn [y#]
(if (pos? ~x) ; just unquote x, not the whole predicate expression
(println y#)
(println (- y#)))))
(macroexpand '(test2 y))
; ==>
; (fn* ([y__1__auto__]
; (if (clojure.core/pos? y)
; (clojure.core/println y__1__auto__)
; (clojure.core/println (clojure.core/- y__1__auto__)))))
(defn test-it []
(let [y -9]
(test2 y)))
((test-it) 5) ; prints "-5"
Feel free to try this with your version. (hint: You'll get an Exception since clojure.lang.Symbol cannot be cast to java.lang.Number)
UPDATE
Since you want to make the function based on a constant you need to write it a little differently:
(defmacro test3 [x]
(assert (number? x) "needs to be a compile time number")
(if (pos? x)
`(fn [y#] (println y#))
`(fn [y#] (println (- y#)))))
Now you'll get an error if you use (test3 x) since x is not a number but get what you want when you evaluate (test3 -10) since -10 is a number we can work with compile time. I'm not sure you'll notice a speed improvement though since these are hardly heavy algorithms.
(define [DML vara]
(cond
((atom? (car vara))
(cond
((eqan? (car vara) 'N)
(display (cdr vara)))
(else (negate vara)))
)))
I'm currently trying to save the content of a return right now for simplicity I was testing the negate function it "returns" a list and I wanted to save the result of that function to do testing. How do I actually save the list return from negate.
Kind of like (x = (negate vara)) where x is the list. I look up let on google and in stack over flow but I can't find a very simple basic assignment.
Excuse my poor syntax on scheme I'm just starting..and going from imperative language to function isn't so smooth..
edit:
I wanted to print out the result expression of (negate vara) but since scheme only prints out the last "recursive call" (pardon my bad wording). I wanted it to use the resulting list from (negate vara) but still print out that list like
say if I had
(else (test (negate vara)))
...
(define (test vara)
(display "test")
)
I wanted it to display
'(O a b)) ;list
here
As you already know, a let expression will do the trick:
(let ((x 10)
(y 20))
(+ x y))
=> 30
The above expression binds values to two variables, x and y. These bindings will exist inside the body of the let. Implicitly, all the expressions in the let form are packed inside a begin, and the end result of the whole expression is the final expression. Notice that one binding cannot refer to the others in the definition part; if one variable needs to refer to previous definitions, then use a let*:
(let* ((x 10)
(y (* x 2)))
(+ x y))
=> 30
Finally, if you need to create a recursive definition use letrec:
(letrec ((fact (lambda (x)
(if (zero? x) 1 (* x (fact (sub1 x)))))))
(fact 10))
=> 3628800
You could make a procedure like you proposed:
(define (test var)
(display var)
var)
(test (negate (test vara)))) ; prints our argument and return
Or you could use DrRacket and use the debugger. You'll miss it after using it once!