Common Lisp: Zip arbitrary number of lists - functional-programming

Suppose you have a list of lists, e.g. '(("abc" "def" "ghi") ("012" "345" "678") ("jkl" "mno" "pqr")) or '(("ab" "cd" "ef") ("01" "23" "45")).
What would be the canonical way to zip the lists inside the given list?
I.e. how would func be defined such that
(func '(("ab" "cd" "ef") ("01" "23" "45")) :sep "|" :combiner #'concat)
;; => ("ab|01" "cd|23" "ef|45")
(func '(("abc" "def" "ghi") ("012" "345" "678") ("jkl" "mno" "pqr")) ...)
;; => ("abc|012|jkl" "def|345|mno" "ghi|678|pqr")
where concat := (lambda (args) ...) is the function that combines the heads of the respective lists.
Presumably, this type of operation is known as rotation or zipMany (according to the answers for related questions for different languages).
I have something like this (double-apply)
(apply #'mapcar #'(lambda (&rest args) (apply #'concatenate 'string args)) lst)
where lst is '(("ab" "cd" "ef") ("01" "23" "45"))
for example. The combiner would be concatenate. Note that no separator is given in this example implementation.
But this seems awfully convoluted.
So, what would be a canonical implementation for this kind of operation?

For an arbitray number of lists, you'd need to get rid of apply #'mapcar, because otherwise the number of lists would be limited by call-arguments-limit.
One typical way would be to use reduce instead, combining two lists at a time:
(defun zip (list-of-lists &key (combiner #'concat))
(reduce (lambda (list-a list-b)
(mapcar combiner list-a list-b))
list-of-lists))
If you don't like the explicit lambda form, you might like curry, e. g. from alexandria:
(defun zip (list-of-lists &key (combiner #'concat))
(reduce (curry #'mapcar combiner)
list-of-lists))
Other looping constructs are loop, do, dolist, and there are also a few looping libraries, e. g. iterate, for.

Join
(defun %d (stream &rest args)
"internal function, writing the dynamic value of the variable
DELIM to the output STREAM. To be called from inside JOIN."
(declare (ignore args)
(special delim))
(princ delim stream))
(defun join (list delim)
"creates a string, with the elements of list printed and each
element separated by DELIM"
(declare (special delim))
(format nil "~{~a~^~/%d/~:*~}" list))
Goal
don't use &rest args or apply - which would limit our list lengths.
Recursive MAPCARS
(defun mapcars (f l)
(if (null (car l))
'()
(cons (funcall f (mapcar #'car l))
(mapcars f (mapcar #'cdr l)))))
Iterative MAPCARS
(defun mapcars (f l)
(loop for l1 = l then (mapcar #'cdr l1)
while (car l1)
collect (funcall f (mapcar #'car l1))))
Usage
CL-USER 10 > (mapcars (lambda (l) (join l "|")) l)
("abc|012|jkl" "def|345|mno" "ghi|678|pqr")

Your function works, but relies on APPLY.
The number of lists you can pass to apply is limited by CALL-ARGUMENTS-LIMIT, which may be large enough in a given implementation. Your function, however, is supposed to accept an arbitrary number of lists, and so, if you want to code portably, you shouldn't use apply in your case.
Zip
The zip function combines all first elements, then all second elements, etc. from a list of lists.
If your input list of lists is the following:
'((a b c d)
(0 1 2 3)
(x y z t))
Then zip will build the following list:
(list (combine (list a 0 x))
(combine (list b 1 y))
(combine (list c 2 z))
(combine (list d 3 t)))
If you look closely, you can see that you can split the work being done as mapping of the combine function over the transpose of your initial list of lists (viewed as a matrix):
((a b c d) ((a 0 x)
(0 1 2 3) ===> (b 1 y)
(x y z t)) (c 2 z)
(d 3 t))
And so, zip can be defined as:
(defun zip (combine lists)
(mapcar #'combine (transpose lists)))
Alternatively, you could use MAP-INTO, if you care about reusing the memory that is allocated when calling transpose:
(defun zip (combiner lists &aux (transposed (transpose lists)))
(map-into transposed combiner transposed))
Transpose
There are different ways to transpose a list of lists. Here I am going to use REDUCE, which is known as fold in some other functional languages. At each step of reduction, the intermediate reducing function will take a partial result and a list, and produce a new result. Our result is also a list of lists.
Below, I show the current list at each step of reduction, and the resulting list of lists.
First step
(a b c d) => ((a)
(b)
(c)
(d))
Second step
(0 1 2 3) => ((0 a)
(1 b)
(2 c)
(3 d))
Third step
(x y z t) => ((x 0 a)
(y 1 b)
(z 2 c)
(t 3 d))
Note that elements are pushed in front of each lists, which explains why each resulting list is reversed w.r.t. the expect result.
The transpose function should thus reverse each list after performing reduction:
(defun transpose (lists)
(mapcar #'reverse
(reduce (lambda (result list)
(if result
(mapcar #'cons list result)
(mapcar #'list list)))
lists
:initial-value nil)))
Like previously, it might be preferable to avoid allocating too much memory.
(defun transpose (lists)
(let ((reduced (reduce (lambda (result list)
(if result
(mapcar #'cons list result)
(mapcar #'list list)))
lists
:initial-value nil)))
(map-into reduced #'nreverse reduced)))
Example
(transpose '(("ab" "cd" "ef") ("01" "23" "45")))
=> (("ab" "01") ("cd" "23") ("ef" "45"))
(import 'alexandria:curry)
(zip (curry #'join-string "|")
'(("ab" "cd" "ef") ("01" "23" "45")))
=> ("ab|01" "cd|23" "ef|45")
Edit: the other answers already provide example definitions for join-string. You could also use the join function from cl-strings.

How about:
(defun unzip (lists &key (combiner #'list))
(apply #'mapcar combiner lists))
Then you should make join:
(defun join (l &key (sep ", "))
(format nil (format nil "~a~a~a" "~{~a~^" sep "~}") l))
Then either make a wrapper or a lambda for your specific use:
(unzip '(("abc" "def" "ghi") ("012" "345" "678") ("jkl" "mno" "pqr"))
:combiner (lambda (&rest l) (join l :sep "|")))
; ==> ("abc|012|jkl" "def|345|mno" "ghi|678|pqr")

Originally, I tried using backquote and splice ,# and defining a macro to get rid of the applys. But as #coredump pointed out, that bears then other problems.
Anyhow, you need the Pythonic function join() as a combiner, if you want to add separators between the joined elements. concatenate will make the things more complicated if the function should behave in the way you want it to.
Since I use #Sylwester's very elegant join() definition, my answer will be very similar to his. Maybe my answer is the closest to your original example.
(defun join (l &key (sep ", "))
(format nil (format nil "~a~a~a" "~{~a~^" sep "~}") l))
Using this, we can define your func to:
(defun func (lists &key (sep "|") (combiner #'join))
(apply #'mapcar
#'(lambda (&rest args) (funcall combiner args :sep sep))
lists))
Or without apply - how #Rainer Joswig points out - and all previous answerers, too - because it has restriction in number of arguments which it can take (~50):
(defun func (lists &key (sep "|") (combiner #'join))
(reduce #'(lambda (l1 l2)
(mapcar #'(lambda (e1 e2)
(funcall combiner (list e1 e2) :sep sep)) l1 l2))
lists))
Or slightly shorter:
(defun func (lists &key (sep "|") (combiner #'join))
(reduce #'(lambda (l1 l2)
(mapcar #'(lambda (&rest l) (funcall combiner l :sep sep)) l1 l2))
lists))
Testing:
(func '(("abc" "def" "ghi") ("012" "345" "678") ("jkl" "mno" "pqr")))
;; ("abc|012|jkl" "def|345|mno" "ghi|678|pqr")
Note that :sep "" makes join equivalent to the function concatenate.
(func '(("abc" "def" "ghi") ("012" "345" "678") ("jkl" "mno" "pqr")) :sep "")
;; ("abc012jkl" "def345mno" "ghi678pqr")
Thanks to #Sylwester, #coredump and #Rainer Joswig!

Why do not use the &rest in params and return that on the lambda function.
CL-USER> (mapcar (lambda (&rest l) l) '(1 2 3) '(a b c) '("cat" "duck" "fish"))
((1 A "cat") (2 B "duck") (3 C "fish"))
then you can use apply like this:
(apply #'mapcar (lambda (&rest l) l) '(("ab" "cd" "ef") ("01" "23" "45")))
; => (("ab" "01") ("cd" "23") ("ef" "45"))
until here, is what a normal zip would do, now you want to join that sublist into one string, with some custom separator.
for that the best answer is use format, and also like this answer
from stackoverflow here https://stackoverflow.com/a/41091118/1900722
(defun %d (stream &rest args)
"internal function, writing the dynamic value of the variable
DELIM to the output STREAM. To be called from inside JOIN."
(declare (ignore args)
(special delim))
(princ delim stream))
(defun join (list delim)
"creates a string, with the elements of list printed and each
element separated by DELIM"
(declare (special delim))
(format nil "~{~a~^~/%d/~:*~}" list))
Then you only need to use mapcar again on this functions and the result list from zip
(mapcar (lambda (lst) (join lst "|")) '(("ab" "01") ("cd" "23") ("ef" "45")))
; => ("ab|01" "cd|23" "ef|45")

Related

Is there a way to implement mapcar in Common Lisp using only applicative programming and avoiding recursion or iteration as programming styles?

I am trying to learn Common Lisp with the book Common Lisp: A gentle introduction to Symbolic Computation. In addition, I am using SBCL, Emacs and Slime.
In chapter 7, the author suggests there are three styles of programming the book will cover: recursion, iteration and applicative programming.
I am interested on the last one. This style is famous for the applicative operator funcall which is the primitive responsible for other applicative operators such as mapcar.
Thus, with an educational purpose, I decided to implement my own version of mapcar using funcall:
(defun my-mapcar (fn xs)
(if (null xs)
nil
(cons (funcall fn (car xs))
(my-mapcar fn (cdr xs)))))
As you might see, I used recursion as a programming style to build an iconic applicative programming function.
It seems to work:
CL-USER> (my-mapcar (lambda (n) (+ n 1)) (list 1 2 3 4))
(2 3 4 5)
CL-USER> (my-mapcar (lambda (n) (+ n 1)) (list ))
NIL
;; comparing the results with the official one
CL-USER> (mapcar (lambda (n) (+ n 1)) (list ))
NIL
CL-USER> (mapcar (lambda (n) (+ n 1)) (list 1 2 3 4))
(2 3 4 5)
Is there a way to implement mapcar without using recursion or iteration? Using only applicative programming as a style?
Thanks.
Obs.: I tried to see how it was implemented. But it was not possible
CL-USER> (function-lambda-expression #'mapcar)
NIL
T
MAPCAR
I also used Emacs M-. to look for the documentation. However, the points below did not help me. I used this to find the files below:
/usr/share/sbcl-source/src/code/list.lisp
(DEFUN MAPCAR)
/usr/share/sbcl-source/src/compiler/seqtran.lisp
(:DEFINE-SOURCE-TRANSFORM MAPCAR)
/usr/share/sbcl-source/src/compiler/fndb.lisp
(DECLAIM MAPCAR SB-C:DEFKNOWN)
mapcar is by itself a primitive applicative operator (pag. 220 of Common Lisp: A gentle introduction to Symbolic Computation). So, if you want to rewrite it in an applicative way, you should use some other primitive applicative operator, for instance map or map-into. For instance, with map-into:
CL-USER> (defun my-mapcar (fn list &rest lists)
(apply #'map-into (make-list (length list)) fn list lists))
MY-MAPCAR
CL-USER> (my-mapcar #'1+ '(1 2 3))
(2 3 4)
CL-USER> (my-mapcar #'+ '(1 2 3) '(10 20 30) '(100 200 300))
(111 222 333)
Technically, recursion can be implemented as follows:
(defun fix (f)
(funcall (lambda (x) (funcall x x))
(lambda (x) (funcall f (lambda (&rest y) (apply (funcall x x) y))))))
Notice that fix does not use recursion in any way. In fact, we could have only used lambda in the definition of f as follows:
(defconstant fix-combinator
(lambda (g) (funcall
(lambda (x) (funcall x x))
(lambda (x) (funcall
g
(lambda (&rest y) (apply (funcall x x)
y)))))))
(defun fix-2 (f)
(funcall fix-combinator f))
The fix-combinator constant is more commonly known as the y combinator.
It turns out that fix has the following property:
Evaluating (apply (fix f) list) is equivalent to evaluating (apply (funcall f (fix f)) list). Informally, we have (fix f) = (funcall f (fix f)).
Thus, we can define map-car (I'm using a different name to avoid package lock) by
(defun map-car (func lst)
(funcall (fix (lambda (map-func) (lambda (lst) ; We want mapfunc to be (lambda (lst) (mapcar func lst))
(if (endp lst)
nil
(cons (funcall func (car lst))
(funcall map-func (cdr lst)))))))
lst))
Note the lack of recursion or iteration.
That being said, generally mapcar is just taken as a primitive notion when using the "applicative" style of programming.
Another way you can implement mapcar is by using the more general reduce function (a.k.a. fold). Let's name the user-provided function f and define my-mapcar.
The reduce function carries an accumulator value that builds up the resulting list, here it is going take a value v, a sublist rest, and call cons with (funcall f v) and rest, so as to build a list.
More precisely, here reduce is going to implement a right-fold, since cons is right-associative (e.g. the recursive list is the "right" hand side, ie. the second argument of cons, e.g. (cons a (cons b (cons nil)))).
In order to define a right-fold with reduce, you pass :from-end t, which indicates that it builds-up a value from the last element and the initial accumulator to obtain a new accumulator value, then the second to last element with that new accumulator to build a new accumulator, etc. This is how you ensure that the resulting elements are in the same order as the input list.
In that case, the reducing function takes its the current element as its first argument, and the accumulator as a second argument.
Since the type of the elements and the type of the accumulator are different, you need to pass an :initial-value for the accumulator (the default behavior where the initial-value is taken from the list is for functions like + or *, where the accumulator is in the same domain as the list elements).
With that in mind, you can write it as follows:
(defun my-map (f list)
(reduce (lambda (v rest) (cons (funcall f v) rest))
list
:from-end t
:initial-value nil))
For example:
(my-map #'prin1-to-string '(0 1 2 3))
; => ("0" "1" "2" "3")

Implementing a dictionary in common lisp

I am trying to implement a dictionary using lists in Common Lisp. The program is supposed to take a list of words and create a word histogram with frequency of each unique word.
This is the program:
(defparameter *histo* '())
(defun scanList (list)
(loop for word in list
do (if (assoc word histo)
((setf pair (assoc word histo))
(remove pair histo)
(setf f (+ 1 (second pair)))
(setf pair ((car pair) f))
(append histo pair))
((setf pair (word '1)) (append histo pair)))))
The error I get is: (SETF PAIR (ASSOC WORD *HISTO*)) should be a lambda expression.
Where is the syntax or semantic error exactly ?
(defun scanList (list the fox jumped over the other fox))
(princ *histo*)
Use hash-table for creating the dictionary and then transform to an association-list (alist) to sort it by key or value.
(defun build-histo (l)
(let ((dict (make-hash-table :test 'equal)))
(loop for word in l
do (incf (gethash word dict))
finally (return dict))))
;; which was simplification (by #Renzo) of
;; (defun build-histo (l)
;; (let ((dict (make-hash-table :test 'equal)))
;; (loop for word in l
;; for count = (1+ (gethash word dict 0))
;; do (setf (gethash word dict) count)
;; finally (return dict))))
(defparameter *histo* (build-histo '("a" "b" "c" "a" "a" "b" "b" "b")))
(defun hash-table-to-alist (ht)
(maphash #'(lambda (k v) (cons k v)) ht))
;; which is the same like:
;; (defun hash-table-to-alist (ht)
;; (loop for k being each hash-key of ht
;; for v = (gethash k ht)
;; collect (cons k v)))
;; sort the alist ascending by value
(sort (hash-table-to-alist *histo*) #'< :key #'cdr)
;; => (("c" . 1) ("a" . 3) ("b" . 4))
;; sort the alist descending by value
(sort (hash-table-to-alist *histo*) #'> :key #'cdr)
;; => (("b" . 4) ("a" . 3) ("c" . 1))
;; sort the alist ascending by key
(sort (hash-table-to-alist *histo*) #'string< :key #'car)
;; => (("a" . 3) ("b" . 4) ("c" . 1))
;; sort the alist descending by key
(sort (hash-table-to-alist *histo*) #'string> :eky #'car)
;; => (("c" . 1) ("b" . 4) ("a" . 3))
The posted code has a whole lot of problems. The reported error is caused by superfluous parentheses. Parentheses can't be added arbitrarily to expressions in Lisps without causing problems. In this case, these are the offending expressions:
((setf pair (assoc word histo))
(remove pair histo)
(setf f (+ 1 (second pair)))
(setf pair ((car pair) f)
(append histo pair))
((setf pair (word '1)) (append histo pair))
In both of these expressions, the results of the calls to setf are placed in the function position of a list, so the code attempts to call that result as if it is a function, leading to the error.
There are other issues. It looks like OP code is trying to pack expressions into the arms of an if form; this is probably the origin of the extra parentheses noted above. But, if forms can only take a single expression in each arm. You can wrap multiple expressions in a progn form, or use a cond instead (which does allow multiple expressions in each arm). There are some typos: *histo* is mistyped as histo in most of the code; f and pair are not defined anyplace; (setf pair (word '1)) quotes the 1 unnecessarily (which will work, but is semantically wrong).
Altogether, the code looks rather convoluted. This can be made much simpler, still following the same basic idea:
(defparameter *histo* '())
(defun build-histogram (words)
(loop :for word :in words
:if (assoc word *histo*)
:do (incf (cdr (assoc word *histo*)))
:else
:do (push (cons word 1) *histo*)))
This code is almost self-explanatory. If a word has already been added to *histo*, increment its counter. Otherwise add a new entry with the counter initialized to 1. This code isn't ideal, since it uses a global variable to store the frequency counts. A better solution would construct a new list of frequency counts and return that:
(defun build-histogram (words)
(let ((hist '()))
(loop :for word :in words
:if (assoc word hist)
:do (incf (cdr (assoc word hist)))
:else
:do (push (cons word 1) hist))
hist))
Of course, there are all kinds of other ways you might go about solving this.

racket programming: how to make bunch of characters into a list?

What I'm currently trying to do is have two sets of integers in a text file be used as parameters for executing a function that calculates the symmetric difference of two sets. The format of the text file is that on first line, there is a set of numbers each separated by a space and on the second line, there is another set of different numbers that's separated by spaces. For example:
1 2 3 4
5 6 1 2
I have successfully implemented a function that is able to read the file and a function that is able to calculate the symmetric difference of two sets, that takes as input of two lists that's interpreted as sets:
function that reads the file:
(define in
(lambda ()
(let ((pin(open-input-file (symbol->string (read)))))
(let g ((x(testchar pin)))
(if (eof-object? x)
(begin
(close-input-port pin)
'())
(write x))
))))
and
function that calculates the symmetric difference:
(define Sym-Dif
(lambda (L1 L2)
(cond ((null? L1) L2)
((union (intersection L1 L2) (intersection L2 L1))))))
In order to use the values from the file, I tried to implement a helper function that examines each characters from the text file such that when it hits the newline character, it recursively builds the first list and when it hits the end of file character, it recursively builds the second list:
helper function:
(define testchar
(lambda(x)
(let f()
(let ((c(read-char x)))
(cond
((eof-object? c) '())
((equal? c #\newline) (begin (list c) (testchar x)))
((char? c) (begin (append c)(write c)(testchar x))))))))
but when running the helper function with function that reads the file, I'm getting #\1#\space#\2#\space#\3#\space#\4#\5#\space#\6#\space#\1#space#\2()
How can I make the helper function to return lists instead of characters?
Any help would be greatly appreciated.
P.s I am using Dr. Racket for this.
"test.txt" contains two lines:
1 2 3 4
5 6 1 2
As sets
How about using the built-in module racket/set?
(require racket/set)
(define (lines->sets filepath)
(let* ((lines (file->lines filepath))
(lists (map (lambda (s) (string-split s " ")) lines)))
(values (list->set (list-ref lists 0)) (list->set (list-ref lists 1)))))
(define-values (set1 set2) (lines->sets "test.txt"))
(set-symmetric-difference set1 set2)
The problem of your code is, it calculates the intersection (union of two intersections) instead of the real symmetric difference (union of differences).
output:
(set "6" "3" "5" "4")
But if you want to have it as lists and not sets:
As lists
(define (lines->lists filepath)
(let* ((lines (file->lines filepath))
(lists (map (lambda (s) (string-split s " ")) lines)))
(values (list-ref lists 0) (list-ref lists 1))))
(define-values (list1 list2) (lines->lists "test.txt"))
;; translated from CL code in http://www.lee-mac.com/listsymdifference.html
(define (list-symmetric-difference l1 l2)
(append
(filter-not (lambda (x) (member x l2)) l1)
(filter-not (lambda (x) (member x l1)) l2)))
(list-symmetric-difference list1 list2)
output:
'("3" "4" "5" "6")

Clisp : select sublists with a given length

Working on CLISP in Sublime Text.
Exp. in CLISP : less than 1 year
It's already for a while that I'm trying to solve this exercice... without success... as you might guess.
In fact I have to create a function which will modify the list and keeps only sublists which are equals or greater than the given number (watch below)
The list on which I have to work :
(setq liste '((a b) c (d) (e f) (e g x) f))
I'm supposed to find this as result :
(lenght 2 liste) => ((a b) (e f) (e g x))
liste => ((a b) (e f) (e g x))
Here my code :
(defun lenght(number liste)
(cond
((atom liste) nil)
((listp (car liste))
(rplacd liste (lenght number (cdr liste))) )
((<= (lenght number (car liste)) number)
(I don't know what to write) )
((lenght number (cdr liste))) ) )
It will be very kind if you could give me only some clue so as to let me find the good result.
Thanks guys.
Modifying the list does not make much sense, because it gets hairy at the head of the list to retain the original reference. Return a new list.
This is a filtering operation. The usual operator in Common Lisp for that is remove-if-not (or remove-if, or remove, depending on the condition). It takes a predicate that should return whether the element should be kept. In this case, it seems to be (lambda (element) (and (listp element) (>= (length element) minlength))).
(defun filter-by-min-length (minlength list)
(remove-if-not (lambda (element)
(and (listp element)
(>= (length element) minlength)))
list))
In many cases, when the condition is known at compile time, loop produces faster compiled code:
(defun filter-by-min-length (minlength list)
(loop :for element :in list
:when (and (listp element)
(>= (length element) minlength))
:collect element))
This returns a new list that fulfills the condition. You'd call it like (let ((minlength-list (filter-by-min-length 2 raw-list))) …).
Many basic courses insist on recursively using primitive operations on cons cells for teaching purposes at first.
The first attempt usually disregards the possible stack exhaustion. At each step, you first look whether you're at the end (then return nil), whether the first element should be discarded (then return the result of recursing on the rest), or if it should be kept (then cons it to the recursion result).
If tail call optimization is available, you can refactor this to use an accumulator. At each step, instead of first recursing and then consing, you cons a kept value onto the accumulator and pass it to the recursion. At the end, you do not return nil, but reverse the accumulator and return that.
Well, I have found the answer that I was looking for, after scratching my head until blood...
Seriously, here is the solution which is working (and thanks for the correction about length which helped me to find the solution ^^) :
(defun filter-by-min-length (min-length liste)
(cond
((atom liste) nil)
((and (listp (car liste))(>= (length (car liste)) min-length))
(rplacd liste (filter-by-min-length min-length (cdr liste))) )
((filter-by-min-length min-length (cdr liste))) ) )
A non-modifying version
(defun filter-by-min-length (min-length le)
(cond ((atom le) nil)
((and (listp (car le)) (>= (length (car le)) min-length))
(cons (car le) (filter-by-min-length min-length (cdr le))))
(t (filter-by-min-length min-length (cdr le)))))
Test:
(defparameter *liste* '((a b) c (d) (e f) (e g x) f))
(filter-by-min-length 2 *liste*)
;; ((A B) (E F) (E G X))
*liste*
;; ((A B) C (D) (E F) (E G X) F) ; -> *liste* not modified
For building good habits, I would recommend to use defparameter instead of setq, since the behaviour of setq might not always be defined (see here). In the link, it is said:
use defvar, defparameter, or let to introduce new variables. Use setf
and setq to mutate existing variables. Using them to introduce new
variables is undefined behaviour

Additional symbol LIST when using ,#

I observed a macro expansion I do not fully understand:
(defmacro test (cons-list)
`(list
,#(mapcar #'(lambda(elem)
elem)
cons-list)))
(defmacro test-2 ()
`(list ,#(list (cons "a" "b"))))
(defmacro test-3 (cons-list)
`(list ,#cons-list))
I'd expect both macros to expand in the same fashion, as I just use mapcar in a fancy way of creating the same list again and then use that list.
But the results observed in SBCL are:
(test (list (cons "a" "b"))) expands to (LIST LIST (CONS "a" "b"))
(test-2) expands to (LIST ("a" . "b"))
(test-3 (list (cons "a" "b"))) again expands to (LIST LIST (CONS "a" "b"))
Why don't these macro expansions behave the same?
Test-2 evaluates the form (list (cons "a" "b")), the other two do not.
Remember: the arguments to a macro are the forms read, unevaluated.
In order to get the same behaviour from test-2, you would have to quote the form: ,#'(list (cons "a" "b")).
EDIT: Here is a step-by-step expansion of test:
`(list
,#(mapcar #'(lambda (elem)
elem)
cons-list))
Removing the backquote syntactic sugar:
(list* 'list (mapcar #'(lambda (elem)
elem)
cons-list)
Argument substitution in your example:
(list* 'list (mapcar #'(lambda (elem)
elem)
'(list (cons "a" "b")))
Evaluate the mapcar form:
(list* 'list '(list (cons "a" "b")))
Evaluate the `list*' form:
'(list list (cons "a" "b"))

Resources