Lambda function with undefined number of arguments - racket

I need to create a function which takes any number of argumnets and returns a comma-separated string of those elements.
E.g.
(comma-separated-list 1 2 3)
;=> "1 , 2 , 3"
This is what I have tried so far:
(define (comma-separated-list x . xs)
(begin
(display x)
(when (not (null? xs))
(begin
(display " , ")
(comma-separated-list xs)))))
But this is not properly working.
As an example:
(comma-separated-list 1 2 3)
returns the string:
1 , (2 3)
while I want it to return:
1, 2, 3
How can I implement it?

Your code:
(define (comma-separated-list x . xs) ; (a) can accept any number of arguments
(begin
(display x) ; (b) print the first argument
(when (not (null? xs))
(begin
(display " , ")
(comma-separated-list xs))))) ; (c) a call with just one argument
After your first call (comma-separated-list 1 2 3), your recursive call on line (c) is equivalent to
(comma-separated-list '(2 3))
which has just a single argument, not two arguments. You need to apply comma-separated-list to the remaining arguments instead:
(apply comma-separated-list xs)
Other Notes
As an aside, there's a difference between what you said you want, what you showed in the desired output, and what your code will generate. Your initial example was
(comma-separated-list 1 2 3)
;=> "1 , 2 , 3"
where there are spaces before the commas, and that's what you'll get with
(display " , ")
but then in the edit, you said that you wanted
1, 2, 3
It's easy to get one or the other (just adjust the spaces), but be aware of the difference!

You could simply use string-join:
(define (comma-separated-list . lst)
(string-join
(map (lambda (e) (format "~a" e)) lst)
", "))
then
(comma-separated-list 1 2 3)
=> "1, 2, 3"

Related

Mapping to a nested list, function applies to quote

I have something like this
(map (lambda (l) (apply + l)) '('(1 2) '(3 4)))
I expect '(3 7), however I get an error saying that the applied function gets applied to a quote.
Why does this happen?
The list I had was equivalent to
(list (quote (list 1 2)) (quote (list 3 4)))
and not
(list (list 1 2) (list 3 4))
The list was malformed. For the difference between list and quote see this post: What is the difference between quote and list?.
A proper way to do nested loop is like '((1 2) (3 4)).

LISP List Not Appending And Printing Twice

I'm quite new to LISP and I am trying to work on the cond statement for class. Currently, I am attempting to check if the value passed is a list and if so, append the letter d onto the list.
Here is my code:
(defun test(L)
(listp L)
(cond ((listp L) (append L (list 'd)))
)
(write L)
)
(test (list 'a 'b 'c))
The output I get is:
(A B C)
(A B C)
If I change the test to: (test (car(list 'a 'b 'c)))
The new output I get is:
A
A
Two things I am wondering is
1.) Why isn't D appended onto the list if the first test passes a list?
2.) Why are they being printed twice? I'm using LISP Works so I figure it's actually something with how it always outputs the final value or something.
1.) The same reason str + "d" doesn't mutate str in Java or Python. It creates a new list that you do not use!
>>> str + "d"
'abcd'
>>> str
'abc'
Crazy similar isn't it?
2.) In CL the return is the last evaluated expression. The REPL prints every top level expression result to the terminal. Python does this too:
>>> def test():
... x = 2 + 3
... print x
... return x
...
>>> test()
5
5
Update
How to mutate the argument list. The simple answer is that you need to mutate the last pair of the argument instead:
(defun test (l)
(assert (consp 1) (l) "l needs to be a non nil list. Got: ~a" l)
(nconc l (list 'd)
(write l)))
(defparameter *test1* (list 1 2 3))
(defparameter *test1-copy* *test1*)
(test *test1*) ; ==> (1 2 3 d) (and prints (1 2 3 d))
*test1* ; ==> (1 2 3 d)
*test1-copy* ; ==> (1 2 3 d)
(eq *test1* *test1-copy*) ; ==> t
(test '())
** error l needs to be a non nil list. Got: NIL
(nconc l x) does (setf (cdr (last l)) x)
If you need to alter the binding, then you need to make a macro:
(defmacro testm (var)
(assert (symbolp var) (var) "List needs to be a variable binding. Got: ~a" var)
`(progn
(when (listp ,var)
(setf ,var (append ,var (list 'd)))
(write ,var))))
(macroexpand '(testm *test2*))
; ==> (progn
; (when (consp *test2*)
; (setf *test2* (append *test2* (list 'd))))
; (write *test2*))
(defparameter *test2* (list 1 2 3))
(defparameter *test2-copy* *test2*)
(testm *test2*) ; ==> (1 2 3 d) (and prints (1 2 3 d))
*test2* ; ==> (1 2 3 d)
*test2-copy* ; ==> (1 2 3)
(eq *test2* *test2-copy*) ; ==> nil
(defparameter *x* nil)
(testm *x*) ; ==> (d) (and prints (d))
*x* ; ==> (d)
(testm '(1))
** error List needs to be a variable binding. Got: '(1)
Idiomatic way to do it
(defun test (list)
(if (consp list)
(append list '(d))
list))
(write (test '(1 2 3)))
; ==> (1 2 3 d) (and prints (1 2 3 d))
(defparameter *test3* '(1 2 3))
(setf *test3* (test *test3*))
*test3* ; ==> (1 2 3 d)

writing my own version of `in` as an Arc macro

In Arc there's a macro called in
> (let x 1
(in x 4 5 6))
nil
> (let x 1
(in x 1 5 6))
t
that checks if its first parameter is equal to any of the rest. I want a version of this that takes a parameter plus a list (semantically identical to Python's in), so I wrote:
(assign weak-tens* '(11 12))
(mac in? (elt lst)
(cons 'in (cons elt lst)))
(def transform-deck (deck)
(map [if (in? _ weak-tens*) #\T _] deck))
output:
arc> (load "main.arc")
*** redefining in?
map: contract violation
expected: list?
given: '(_ . weak-tens*)
argument position: 2nd
other arguments...:
#<procedure:ac-niltree>
To answer your immediate question, you can use mem, as follows:
arc> (let x 3
(mem x '(1 2 3 4)))
(3 4)
Also, there's no reason this should be a macro. It doesn't do anything that requires a macro.
But let's look at why the macro doesn't work:
arc> (macex1 '(in? 1 '(1 2 3)))
(in 1 quote (1 2 3))
Ah, we're putting the value "quote".
Here's how we want the code to expand:
(in? 1 '(1 2 3))
should expand to:
(in 1 1 2 3)
But as mentioned, we don't even want this to be a macro in the first place. Ignoring mem, it could be written as follows:
(def in? (elt lst)
(if (no lst)
nil
(is elt ;;if we've found the element
(car lst))
t
(in? elt (cdr lst)))) ;;otherwise recurse

Element not being added to list

(defparameter *todo* '("Conquer the world" "Bake cake"))
(defun how-many-items (list)
if (list
(1+ (how-many-items (cdr list)))
0))
(defun add-item (item)
(cons item *todo*)) ; Attempt to add an item to the todo list
(princ (how-many-items *todo*))
(princ '#\newline)
(add-item "Write a book")
(princ (how-many-items *todo*))
(princ '#\newline)
(princ (cdr *todo*))
(princ '#\newline)
I'm still learning Lisp but I can't understand why the size of the list doesn't add when I supposedly add the item "Write a book" to it, the cdr call returns "Bake Cake" and the number of items is always two.
The output is:
2
2
(Bake cake)
Your problem is that cons is non-destructive. This means that even though you're adding an item to the item to the list that *todo* contains, you're not modifying *todo*
> (defparameter x '(1 2 3))
(1 2 3)
> (cons 1 x)
(1 1 2 3)
> x
(1 2 3)
See? No modification.
Instead use push. It does modify its parameters.
> (defparameter x '(1 2 3))
(1 2 3)
> (push 1 x)
(1 1 2 3)
> x
(1 1 2 3)
You can think of push like this
(push x 1) === (setf x (cons 1 x))
In fact, it's a macro that expands to just this in some implementations.
Your output can't be real, since your function has wrong syntax.
(defun how-many-items (list)
if (list
(1+ (how-many-items (cdr list)))
0))
CL-USER 20 > (how-many-items '(1 2))
Error: The variable IF is unbound.

Scheme Macros - Pair in the transform but list as output?

Let's say I have the following macro in R5RS Scheme:
(define-syntax pair-test
(syntax-rules ()
((_ (a b . c))
(quote (a b . c)))))
The macro transforms an input pair to an output pair, as one would expect:
(pair-test (1 2 . 3))
==> (1 2 . 3)
I can also pass a list to the macro, as allowed by the spec. However, the output is a list instead of a pair:
(pair-test (1 2 3))
==> (1 2 3)
What exactly is going on here? Why is the output a list instead of a pair?
Could c be (3 . ()) in the second case? I'm not positive but that would make sense to me. And then quoting (a b . c) would be (1 2 . (3 . ())) which is (1 2 . (3)) and (3) is a proper list, so (1 2 3)?
To see what's happening here, you need to know that a list in Scheme is a recursive chain of pairs of elements and other lists. Any data that follows the form of a list will always be printed as a list. Once you know how basic lists are constructed, you'll be able to see what's happening inside your macro.
Pairs in Scheme can be created using the . operator, or using the cons function. Here's a simple pair of numbers:
(quote (1 . 2))
==> '(1 . 2)
(cons 1 2)
==> '(1 . 2)
To create a list of 1 in Scheme, you can make a pair out of something and the empty list:
(quote (1 . ()))
==> '(1)
(cons 1 (list))
==> '(1)
A list of 2 is a pair of something of something on the left side, and a list of 1 on the right side. Likewise, a list of 3 is one element paired with a list of 2:
(quote (1 . (2 . (3 . ()))))
==> '(1 2 3)
(cons 1 (cons 2 (cons 3 (list))))
==> '(1 2 3)
To see what your macro is doing, you can rearrange the (quote (a b . c)) to be more explicit:
(quote (a . (b . c)))
(cons (quote a) (cons (quote b) (quote c)))
Now you can see that this form looks very similar to when you're constructing a list. If (quote c) results in a list, then the whole expression will be a list. In the case of (pair-test (1 2 3)), c becomes (3 . ()):
(quote (a . (b . c)))
==> (quote (1 . (2 . (3 . ()))))
==> '(1 2 3)
(cons (quote a) (cons (quote b) (quote c)))
==> (cons '1 (cons '2 '(3 . ())))
==> '(1 2 3)
This value is printed by the REPL as a list because it is a "proper list". Each right side (cdr) is a list, all the way up to the empty list at the end, so this value perfectly follows list form. The REPL assumes you'd like to see the result as a list, so it gets printed without a . present.
You'd see '(1 2 . 3) for (pair-test (1 2 . 3)), because this is how the REPL prints "improper lists". If the last element in the chain of pairs is not the empty list, the value is considered an "improper list", and will be printed differently:
(quote (1 . (2 . 3)))
==> '(1 2 . 3)
(cons 1 (cons 2 3))
==> '(1 2 . 3)