I have a question about lisp macros. I want to check a condition and, if it's true, to run a few things, not only one. The same for the false way. Can anybody please help??
(defmacro test (condition (&rest then) (&rest else))
`(if ,condition (progn ,#then) (progn ,#else)))
(test (= 4 3)
(print "YES") (print "TRUE")
(print "NO") (print "FALSE"))
The usual way to test macros is to use macroexpand.
Your test case misses parens (lisp reader does not care about whitespace, such as line breaks and indentation):
(macroexpand '(test (= 4 3)
(print "YES") (print "TRUE")
(print "NO") (print "FALSE")))
Error while parsing arguments to DEFMACRO TEST:
too many elements in
((= 4 3) (PRINT "YES") (PRINT "TRUE") (PRINT "NO") (PRINT "FALSE"))
to satisfy lambda list
(CONDITION (&REST THEN) (&REST ELSE)):
exactly 3 expected, but got 5
while
(macroexpand '(test (= 4 3)
((print "YES") (print "TRUE"))
((print "NO") (print "FALSE"))))
(IF (= 4 3)
(PROGN (PRINT "YES") (PRINT "TRUE"))
(PROGN (PRINT "NO") (PRINT "FALSE")))
Please note that CL has cond
which is usually used when one needs multiple forms in
if clauses.
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
Beeing new to CL and having tried several mapping approaches I still couldn't figure it out.
Is it possible to collect from a nested list all first elements as shown below? Conceptually speaking:
How can one gather a parent node and all its child-nodes from a (not necessarily binary) tree-like structure in CL.
(defparameter *nested-list* (list "a" (list "b" (list "c" "d"))
(list "e" (list "f")
(list "g" "h"))))
The function call...
(defun collect-firsts (*nested-list*)
; ...
)
...should result in something like this:
:-> (('start "a") ("a" "b" "e") ("b" "c") ("c" "d")
("e" "f" "g")("f") ("g" "h"))
Thanks in advance!
How about something like this?
(defun immediate-children (object &aux (result '()))
(labels ((f (object)
(cond
((consp object)
(push (list* (first object)
(mapcar #'f (rest object)))
result)
(first object))
(t object))))
(f object)
result))
CL-USER> (immediate-children *nested-list*)
(("a" "b" "e") ("e" "f" "g") ("g" "h") ("f") ("b" "c") ("c" "d"))
The result isn't exactly what you provided in the question, but I think it still makes sense. It includes ("f") in the results, which is probably reasonable, since there's a node labeled "f" with no children. The traversal order is different too, but if this is just a list of lists of the form (parent child+), then that's probably not a problem.
I have tested the following in emacs and in common-lisp.
The output can be inspected at https://ideone.com/hNEQTJ.
(defparameter *nested-list* (list "a" (list "b" (list "c" "d"))
(list "e" (list "f")
(list "g" "h"))))
(defun collect-firsts (*nested-list*)
(let ((ret `((start ,(car *nested-list*)))))
(labels ((recursion (x)
;; 1st generate list of the name and the cars
(setq ret (append ret
(list (cons (car x)
(mapcar (lambda (el)
(if (listp el) (car el) el))
(cdr x))))))
;; 2nd recurse if required
(mapc (lambda (el)
(when (and (listp el) (cdr el))
(recursion el))) x)
ret))
(recursion *nested-list*)
ret)))
(collect-firsts *nested-list*)
I want to do this in Clojure:
int i=1;j=2;k=3;
str r;
cin>>r;
if(r=="A")
cout<<i; (i.e., print 1)
if(r=="J")
cout<<j; (i.e., print 2)
if(r=="K")
cout<<k; (i.e., print 3)
else
do something else
I am doing it like this in clojure:
(defn str-to-num [c]
(if ( = (str (first c )) "A")
1
(java.lang.Integer/valueOf (str (first c))))
(if ( = (str (first c )) "J")
2
(java.lang.Integer/valueOf (str (first c))))
(if ( = (str (first c )) "K")
3
(java.lang.Integer/valueOf (str (first c))))
)
But, I'm getting an error. Could someone tell what I'm doing wrong?
All of the if blocks are run, one after the other, regardless of what any of them return. If you want if / else / chaining you should use cond or case (though the two branches of a standard if work fine if there are only two options).
Your first two if blocks can't do anything meaningful except throw an error. And that is exactly what will happen for most inputs.
"A", "J", and "K" are not valid numbers, so trying to parse them will throw an error.
The only meaningful things this function can do is return the first letter of a string as a number if it is parsible as one.
user> (str-to-num "A")
NumberFormatException For input string: "A" java.lang.NumberFormatException.forInputString (NumberFormatException.java:65)
user> (str-to-num "J")
NumberFormatException For input string: "J" java.lang.NumberFormatException.forInputString (NumberFormatException.java:65)
user> (str-to-num "K")
NumberFormatException For input string: "K" java.lang.NumberFormatException.forInputString (NumberFormatException.java:65)
user> (str-to-num "100")
1
perhaps you wanted something like:
user> (defn str-to-num [c]
(case (first c)
\A 1
\J 2
\K 3
(int (first c))))
#'user/str-to-num
user> (str-to-num "A")
1
user> (str-to-num "J")
2
user> (str-to-num "K")
3
user> (str-to-num "L")
76
user> (str-to-num "☃")
9731
Alternately:
user> (defn str-to-num [c]
(case (first c)
\A 1
\J 2
\K 3
(Integer/parseInt (subs c 0 1))))
#'user/str-to-num
user> (str-to-num "9")
9
The problem is with the form of your if statement
You have
(if ( = (str (first c )) "A")
1
(java.lang.Integer/valueOf (str (first c))))
The form of if is
(if (cond)
trueResult
falseResult)
So your "working version" will return 1 if you input A. If you input any other string, it actually throws an error. But, if an error were not thrown, all three if statements would be executed, and the result of the last one is actually returned.
This is closer to your C++ code:
(defn str-to-num [c]
(if ( = (str (first c )) "A") (print 1))
(if ( = (str (first c )) "J") (print 2))
(if ( = (str (first c )) "K") (print 3)))
This question might be so easy, but I spent some time on it and I could not figure out what the problem is. I am new to Scheme ;)
Okay, this is my code:
(define foo
(lambda (x)
((if(equal? (car x) 4) "A" "B")
(if(equal? (car (cdr x)) 3) "A" "B")
(if(equal? (car(cdr (cdr x))) 5) "A" "B")
)
))
(foo '(4 3 5))
I get the following error, when I run the code:
> application: not a procedure; expected a procedure that can be
> applied to arguments given: "A" arguments...: "A" "A"
To fix the error, simply do this:
(define foo
(lambda (x)
(if (equal? (car x) 4) "A" "B")
(if (equal? (car (cdr x)) 3) "A" "B")
(if (equal? (car (cdr (cdr x))) 5) "A" "B")))
The problem? that in your code there's an extra and erroneous pair of () surrounding the if expression, so Scheme thinks that if is returning a function (which is not). Remember, in Scheme this: (f) means: execute f as a function with no arguments.
Now that we found the syntax error, let's see what else is wrong with the code. For starters, if you write a sequence of expressions inside a procedure only the value of the last one is returned, so the first two ifs are being completely ignored! After reading the comments I understand that you want to return "AAA" for the input '(4 3 5), or "ABA" for input '(4 6 5). The trick here is that we must use string-append to stick together all the strings:
(define foo
(lambda (x)
(string-append
(if (equal? (car x) 4) "A" "B")
(if (equal? (cadr x) 3) "A" "B")
(if (equal? (caddr x) 5) "A" "B"))))
Now it works as expected:
(foo '(4 3 5))
=> "AAA"
(foo '(4 6 5))
=> "ABA"
Let me start off by saying, "Welcome to Scheme!" Don't get frustrated and whatever you do, keep on
trying to understand the functional-programming way of doing things.
Your perseverance will pay off, and you will become a better programmer
for the effort, even when you go back to using Java.
Scheme is a good place to start learning functional programming.
I hope the following will help you to see how you might start to
change your thinking, so here goes:
Here is a bit about why you got the weird error you saw. If you
have:
(define foo
(lambda (x)
((if(equal? (car x) 4) "A" "B")
(if(equal? (car (cdr x)) 3) "A" "B")
(if(equal? (car(cdr (cdr x))) 5) "A" "B")
)
))
Then:
(foo '(4 3 5))
is equal to:
((if (equal? (car '(4 3 5)) 4) "A" "B"))
(if (equal? (car (cdr '(4 3 5))) 3) "A" "B")
(if (equal? (car (cdr (cdr '(4 3 5)))) 5) "A" "B"))
Because that is what you get when you replace every occurrence
of x with (4 3 5) in the body of the lambda-expression you wrote.
This next bit of reasoning is a bit of a shortcut, but notice that:
(car '(4 3 5)) = 4
AND
(car (cdr '(4 3 5))) = (car '(3 5)) = 3
AND
(car (cdr (cdr '(4 3 5)))) = (car (cdr '(3 5))) = (car '(5)) = 5
So, when we substitute equals for equals in the above expression,
we get:
((if (equal? 4 4) "A" "B")
(if (equal? 3 3) "A" "B")
(if (equal? 5 5) "A" "B"))
and this is the same as:
("A" "A" "A")
This is a peculiar Scheme expression, and the reason Scheme complains is
because you're trying to invoke a function called "A" and apply it to
two arguments, "A" and "A".
From the discussion above, it's pretty clear that you're trying to
derive a new structure, based on the input. So, what you need is
a function which derives the new structure, based on an examination
of the existing structure, and assembles a new one. This is what
is returned by the function.
Scheme is a functional language, which means that it is very
expression-oriented. In Scheme, the computer is turned into a kind of
mechanical expression-evaluator. This is very different from
what we became used to in object-oriented languages (e.g., Java,
C#) because those languages are all about carefully controlling
changeable state. In functional languages, it's all about
calculating the value of an expression. (By the way, In a deep
and beautiful way, it just so happens that everything you can
do in the world of "mutable state programming" can be done in
the world of "mechanical evaluation of expressions." There's a
deep and fundamental equivalence between the two, believe it or not.)
If you are trying to create a new list, based on tests of the individual
elements in list (4 3 5), then you need to write expressions which do
two separate things: construct lists from smaller things, and examine
the various elements of the list (in this case, 4, 3, and 5.)
(define foo
(lambda (x)
(cons (if (equal? (car x) 4) "A" "B")
(cons (if (equal? (car (cdr x)) 3) "A" "B")
(cons (if (equal? (car (cdr (cdr x))) 5) "A" "B") '() )))))
So, (foo '(4 3 5)) results in ("A" "A" "A"), which is a list of strings.
You eventually want to append them all together into a single string,
right? So, there's already a function called fold-right which provides
a way to do this. The fold-right function can use the string-append
function to accomplish this:
(fold-right string-append "" (foo '(4 3 5)))
which should provide an answer of "AAA".
(fold-right string-append "" (foo '(4 4 4)))
gives an answer of "ABB"
Using functions like fold, fold-right, etc, is a more of a functional
programming way to do these kinds of things.
Depending on the circumstances, you may want to use those folding functions
instead of writing the foo function in terms of the string-append function, as
Oscar Lopez did above.
Like I said earlier, you have to think about things very differently
when using a functional programming language, and get away from thinking
of programs as sequences of operations which change the internal state
of the computer's memory. You have to start thinking in terms of the
computer as a powerful expression evaluation machine instead.
I hope this helps, and is an encouragement to you to keep working at learning Scheme.
Because the body of your lambda is wrapped in parentheses, the first if is being treated as a procedure, and when it tries to apply the results of the other if's to it, you get the error you got.
If you are trying to return a list of the values of those 3 if's, then you should use the list function.
I have a problem with common lisp.
I want to pass a string to a function
and want that this strings become a structure.
I can't use external library.
For example with this input:
(testfu "ftp/http.ok:3345")
This is the struct:
(defstruct test-struct
scheme
part
ans
port)
I want this result:
scheme: "ftp" part: "http" ans "ok" port "3345"
How can I do the testfu ?
here my bad try :(
(defun testfu (x)
(setq ur1 (make-test-struct :scheme frist x :host second x)))
I'd recommend using a regex to parse this. Using CL-PPCRE which is the Common Lisp regex library, the code would look like this:
(defun testfu (x)
(multiple-value-bind (result values)
(ppcre:scan-to-strings "^([a-z]+)/([a-z]+)\\.([a-z]+):([0-9]+)$" x)
(unless result
(error "String ~s is not valid" x))
(make-test-struct :scheme (aref values 0)
:part (aref values 1)
:ans (aref values 2)
:port (aref values 3))))
Note that you probably would have to adjust the regex to better represent the actual format of the input string, in particular if any of the fields are optional.
You will have to parse the data out of the string in order that you might use it for your strut. Lisp won't do that magically.
Split Sequence is a good library for doing that
If you don't want a library, then some code to get you on the correct track. This will tokenize string based on a predicate function fn ( which returns true when a character is a delimiter and false otherwise )
(defun split-by-fn (fn string)
(let* ((STARTING 0)
(TOKEN 1)
(DELIM 2)
(state STARTING)
(a-token "")
(the-list '())
(str-length (length string)))
(dotimes (i str-length)
(if (funcall fn (char string i))
(progn
(if (eq state TOKEN)
(progn
(setq the-list (cons a-token the-list))
(setq a-token "")))
(setq state DELIM))
(progn
(setq a-token
(concatenate 'string a-token (string (char string i))))
(setq state TOKEN))))
(if (eq state TOKEN)
(setq the-list (cons a-token the-list)))
(setq the-list (reverse the-list))))
I don't usually write code for people but here is an example parser, it's not the most lisp-y, there are better ways of doing this, but it works.
(defun parser ( string )
(labels ((set-field (state struct token)
(let ((SCHEME 0)
(PART 1)
(ANS 2)
(PORT 3))
(cond ((= state SCHEME)
(setf (example-struct-SCHEME struct) token))
((= state PART)
(setf (example-struct-PART struct) token))
((= state ANS)
(setf (example-struct-ANS struct) token))
((= state PORT)
(setf (example-struct-PORT struct) token))))))
(let ((state 0)
(token "")
(check 0)
(a-list '())
(struct (make-example-struct)))
(loop for char across string do
(progn
(setq check (position char "/.:"))
(if check
(progn
(set-field state struct token)
(setq token "")
(setq state (+ check 1)))
(setq token (concatenate 'string token (string char))))))
(progn
(if (/= 0 (length token))
(set-field state struct token))
struct))))