Understanding loop macro expansion - loops

I expanded the macro below to see how it worked and found myself a little confused.
(loop for i below 4 collect i)
expands to (I have cleaned it up a little for readability)
(block nil
(let ((i 0))
(declare (type (and number real) i))
(let* ((list-head (list nil))
(list-tail list-head))
(tagbody
sb-loop::next-loop
(when (>= i 4) (go sb-loop::end-loop))
(rplacd list-tail (setq list-tail (list i)))
(setq i (1+ i))
(print "-------") ;; added so I could see the lists grow
(print list-head)
(print list-tail)
(print "-------")
(go sb-loop::next-loop)
sb-loop::end-loop
(return-from nil (cdr list-head))))))
..and here is the output from running the above..
;; "-------"
;; (NIL 0)
;; (0)
;; "-------"
;; "-------"
;; (NIL 0 1)
;; (1)
;; "-------"
;; "-------"
;; (NIL 0 1 2)
;; (2)
;; "-------"
;; "-------"
;; (NIL 0 1 2 3)
;; (3)
;; "-------"
I just can't see where list-head is modified, I have to assume head and tail are eq and thus modifying one is modifying the other but could someone please break down what is happening on the rplacd line?

list-head and list-tail are initially the same (in the eq sense). list-head is a cons which cdr is the list being collected. list-tail points to the last cons in the list (except initially, see below).
To add an element to the end of the list, replacd modifies the cdr of list-tail to add a new cons, and list-tail is updated to point to the new cons.
When the loop terminates, the result is the cdr of list-head.
Why this complicated business with an extra cons? Because the list appending algorithm becomes easier when list-tail is always a pointer to the last cons of the list. But in the beginning, the empty list has no cons. So the trick is to make the list one cons longer.

Related

Using loop inside defmacro

I'm learning (common) Lisp, and as exercise, I want to implement 'xond', a cond macro, that transform this silly example:
(xond (= n 1) (setq x 2) (= n 2) (setq x 1))
into a if-else chain:
(if (= n 1) (setq x 2) (if (= n 2) (setq x 1)))
Currently, I have this macro:
(defmacro xond (&rest x) (if x (list 'progn (list 'if (pop x) (pop x)))))
that just expand the first two items in x:
(macroexpand '(xond (= x 1) (setq y 2)))
produce
(PROGN (IF (= X 1) (SETQ Y 2))) ;
Now I want to process all items in x, so I add a loop to produce a if-serie (a step toward if-else-version):
(defmacro xond (&rest x)
(loop (if x
(list 'progn (list 'if (pop x) (pop x)))
(return t))))
but then macro seems to stop working:
(macroexpand '(xond (= x 1) (setq y 2)))
T ;
What I'm missing here?
Edition
verdammelt's answer put me in the right track, and coredump's made me change my approach to an iterative one.
Now I'll implement (xond test1 exp1 test2 exp2) as:
(block nil
test1 (return exp1)
test2 (return exp2)
)
which can be done by iteration.
I'm writing this for my minimal Lisp interpreter; I have only implemented the most basic functions.
This is what I wrote. I'm using la to accumulate the parts of the output.
(defmacro xond (&rest x)
(let ((la '()))
(loop
(if x (push (list 'if (pop x) (list 'return (pop x))) la)
(progn (push 'nil la)
(push 'block la)
(return la)
)))))
with
(macroexpand '(xond (= x 1) (setq y 2) (= X 2) (setq y 1)))
result:
(BLOCK NIL
(IF (= X 2) (RETURN (SETQ Y 1)))
(IF (= X 1) (RETURN (SETQ Y 2)))
) ;
Second edition
Add a label to block and change return to return-from, to avoid conflict with other return inside arguments. Also changed push for append to generate code in the same orden as the parameters.
(defmacro xond (&rest x)
(let ((label (gensym)) (la '()) (condition nil) (expresion nil))
(setq la (append la (list 'block label)))
(loop
(if x
(setq la (append la (list
(list 'if (pop x) (list 'return-from label (pop x))))))
(return la)))))
So
(macroexpand '(xond (= x 1) (setq y 2) (= X 2) (setq y 1)))
now gives
(BLOCK #:G3187 (IF (= X 1) (RETURN-FROM #:G3187 (SETQ Y 2))) (IF (= X 2) (RETURN-FROM #:G3187 (SETQ Y 1))))
Some remarks
You do not need a progn when you only expand into a single if
The use of pop might be confusing for the reader (and the programmer too) since it mutates a place, maybe you want to start with a less imperative approach
Also, in that case I don't think a loop approach is helpful, because you need to nest the expressions that come after in the body inside a previously built form, and even though it can be done, it is a bit more complex to do that simply a recursive function or a "recursive" macro.
Here I explain both approach, starting with "recursive" macro (the quote here is because the macro does not call itself, but expands as call to itself).
Macro expansion fixpoint
If I had to implement xond, I would write a macro that expands into other calls to xond, until macroexpansion reaches a base case where there are no more xond:
(defmacro xond (&rest body)
(if (rest body)
(destructuring-bind (test if-action . rest) body
`(if ,test ,if-action (xond ,#rest)))
(first body)))
For example, this expression:
(xond (= n 1) (setq x 2) (= n 2) (setq x 1))
First macroexpands into:
(if (= n 1)
(setq x 2)
(xond (= n 2) (setq x 1)))
And eventually reaches a fixpoint with:
(if (= n 1)
(setq x 2)
(if (= n 2)
(setq x 1)
nil))
Be careful, you cannot directly use xond inside the definition of xond, what happens is that the macro expands as a call to xond, which Lisp then expands again. If you are not careful, you may end up with an infinite macroexpansion, that's why you need a base case where the macro does not expand into xond.
Macro calling a recursive function
Alternatively, you can call a recursive function inside your macro, and expand all the inner forms at once.
With LABELS, you bind xond-expand to a recursive function. Here this is an actual recursive approach:
(labels ((xond-expand (body)
(if body
(list 'if
(pop body)
(pop body)
(xond-expand body))
nil)))
(xond-expand '((= n 1) (setq x 2) (= n 2) (setq x 1))))
; => (IF (= N 1)
; (SETQ X 2)
; (IF (= N 2)
; (SETQ X 1)
; NIL))
Your xond macro ends with (return t) so it evaluates to t rather than your accumulated if expressions.
You could use loop's collect clause to accumulate the code you wish to return. For example: (loop for x in '(1 2 3) collect (* 2 x)) would evaluate to (2 4 6).
How about
(ql:quickload :alexandria)
(defun as-last (l1 l2)
`(,#l1 ,l2))
(defmacro xond (&rest args)
(reduce #'as-last
(loop for (condition . branch) in (alexandria:plist-alist args)
collect `(if ,condition ,branch))
:from-end t))
(macroexpand-1 '(xond c1 b1 c2 b2 c3 b3))
;; (IF C1 B1 (IF C2 B2 (IF C3 B3))) ;
;; T
alexandria's plist-alist was used to pair the arguments,
the intrinsic destructuring in loop used to extract conditions and branches.
The helper function as-last stacks lists together in the kind of
(a b c) (d e f) => (a b c (d e f)).
(reduce ... :from-end t) right-folds the sequence of the collected (if condition branch) clauses stacking them into each other using #'as-last.
Without any dependencies
('though, does alexandria even count as a dependency? ;) )
(defun pairs (l &key (acc '()) (fill-with-nil-p nil))
(cond ((null l) (nreverse acc))
((null (cdr l)) (pairs (cdr l)
:acc (cons (if fill-with-nil-p
(list (car l) nil)
l)
acc)
:fill-with-nil-p fill-with-nil-p))
(t (pairs (cdr (cdr l))
:acc (cons (list (car l) (cadr l)) acc)
:fill-with-nil-p fill-with-nil-p))))
(defun as-last (l1 l2)
`(,#l1 ,l2))
(defmacro xond (&rest args)
(reduce #'as-last
(loop for (condition branch) in (pairs args)
collect `(if ,condition ,branch))
:from-end t))
(macroexpand-1 '(xond c1 b1 c2 b2 c3 b3))
;; (IF C1 B1 (IF C2 B2 (IF C3 B3))) ;
;; T
The helper function pairs makes out of (a b c d e f) => ((a b) (c d) (e f)).
(:fill-with-nil-p determines in case of odd number of list elements, whether the last element would be listed (last-el) or (last-el nil) - in the latter case filled with nil).

Common Lisp vector printing: Vector axis is not zero: 1

This is the first time I have ever seen this compiler error does anyone know what it is? and why my function can't print my nested vectors.
(defvar *col-lookup* #.(let ((ht (make-hash-table)))
(loop for (key . value) in
'(
(A . 0) (B . 1) (C . 2)
(D . 3) (E . 4) (F . 5) (G . 6))
do (setf (gethash key ht) value))
ht))
;; vector of vectors
(defparameter *game-board*
(make-array 7 :initial-element (make-array 0 :initial-element 0)))
;;make move lookup character in table then push X onto vector of value of key
(defun move (c)
(let ((place (gethash c *col-lookup*)))
(cond ((oddp *turn-count*)
(push "X" (aref *game-board* place))
(incf *turn-count*))
((push "O" (aref *game-board* place))
(incf *turn-count*)))))
You are creating a very peculiar vector of vectors with the code:
(make-array 7 :initial-element (make-array 0 :initial-element 0)))
This code will create a vector of 7 elements, each of them a vector with 0 elements (i.e. an empty vector) (and note that giving the initial-element to 0 is useless because there are no elements to assign). If you print it you should see:
#(#() #() #() #() #() #() #())
which means exactly this, a vector with seven empty vectors. So if you try to access the internal vector with something like (aref (aref *game-board*) 1) 2) you get an error.
Finally note that in the code of the function move you use:
(push "X" (aref *game-board* place))
whose effect is not of modifying the internal vector at place place, but of replace the old value of (aref *game-board* place) with a cons of the string "X" and the old value of (aref *game-board* place), the empty vector.
I was able to print my vector of vectors by simply looping over it once. I still do not know what that error was but I haven't ran into it since.
(defun print-game ()
(loop for i from 0 to 6 do
(print (aref *game-board* i))))

collecting multiple maximum values

I have a list of elements. Each element is structured as followed:
('symbol "string" int-score)
An example list:
(list (list 'object1 "wabadu" 0.5)
(list 'object2 "xezulu" 0.6)
(list 'object1 "yebasi" 0.5)
(list 'object1 "tesora" 0.2))
I want to retrieve the maximum values for a specific symbol. When I search with the symbol object2, I should get back:
('object2 "xezulu" 0.6)
If I search with object1, I should get back:
(('object1 "wabadu" 0.5) ('object1 "yebasi" 0.5))
I want to collect all the highest elements of a specific object. What I can do is this: assume that the above list is the list used below and that I'm searching for object1. I can retrieve all elements of a specific object:
(loop for element in list
when (equal 'object1 (first element))
collect element)
I can also retrieve one highest element of the list:
(loop for element in list
when (equal 'object1 (first element))
maximize (third element))
However, this will only return one element. What I want is all maximum elements. I've tried some combinations with collect and maximize, but my knowledge on the syntax is little. Is there a way to collect all the highest elements in a ‘simple’ function?
A sketch of a LOOP-based version:
(defun mymax (target list &aux result max)
(loop for (item name value) in list
when (eql item target)
do (cond ((or (null result)
(> value max))
(setf result (list (list item name value))
max value))
((= value max)
(push (list item name value) result))))
result)
This will create a hash-table with the keys being the symbols and the values being arranged in the way (maximum . (list of strings corresponding to maximum))
(let ((data (list (list 'object1 "wabadu" 0.5)
(list 'object2 "xezulu" 0.6)
(list 'object1 "yebasi" 0.5)
(list 'object1 "tesora" 0.2))))
(loop
:with table := (make-hash-table)
:for (item string num) :in data :do
(destructuring-bind (&optional max strings)
(gethash item table)
(cond
((or (null max) (< max num))
(setf (gethash item table) (list num (list string))))
((= max num)
(setf (cdr strings) (cons string (cdr strings))))))
:finally (return table)))
;; #<HASH-TABLE {1005C6BE93}>
;; --------------------
;; Count: 2
;; Size: 16
;; Test: EQL
;; Rehash size: 1.5
;; Rehash threshold: 1.0
;; [clear hashtable]
;; Contents:
;; OBJECT1 = (0.5 ("wabadu" "yebasi")) [remove entry]
;; OBJECT2 = (0.6 ("xezulu")) [remove entry]
I think your life would be later easier with this hash table then with the data structure you currently have.
You can do that by looping through the list once for selecting all the sublists with the right first elements and determining the maximum (you can use into to let loop accumulate multiple values), and then a second loop in the finally clause go through the selection and now select only those with the maximum score:
(loop for triple in *l*
for (key nil score) = triple
when (eq key 'object1)
collect triple into selection
and maximize score into max-score
finally (return (loop for triple in selection
when (eql (third triple) max-score)
collect triple)))
Edit: Alternatively, instead of a second loop, the delete function can be used here quite concisely:
(loop for triple in *l*
for (key name score) = triple
when (eq key 'object1)
collect triple into selection
and maximize score into max-score
finally (return (delete max-score selection
:test #'/=
:key #'third)))
The maximize returns only one element. You can sort all the list by the 3rd component and then gets the front one(s). Like this:
;;; suppose a copy of the data is stored in l
;; get all 'object1 and sort them
(setf l (sort (remove-if-not
(lambda (x) (equal (first x) 'object1)) l)
#'> :key #'third))
;; remove the ones with smaller value than the first one
(setf l (remove-if
(lambda (x) (< (third x) (third (first l)))) l))
Abstract your data to create basic building blocks; combine building blocks into your needed functionality:
(defun make-foo (type name score)
(list type name score))
(defun foo-type (foo) (elt foo 0))
;; ...
(defun make-foos (&rest foos)
foos)
(defun foos-find-if (foos predicate)
;; return all foos satisfying predicate
)
(defun foos-maximize (foos orderer)
;; return the maximum foo (any one)
)
(defun foos-find-if-maximized (foos)
(foos-find-if foos
(let ((max (foos-maximize foos #'foo-score)))
(lambda (foo)
(= (foo-score max) (foo-score foo))))))
Here is an approach by first saving symbol-list that only contains the lists with the search object. Then we can easily get the maximum value and remove those lists with a smaller value.
(defun foo (symbol list)
(let* ((symbol-list (remove-if-not #'(lambda (l) (eq (first l) symbol))
list))
(max (apply #'max (mapcar #'third symbol-list))))
(remove-if-not #'(lambda (l) (= (third l) max))
symbol-list)))
We can call it: (foo 'object1 l)
As a rule of thumb, if you are really wanting to boil down a list of things into a single result, there should be a nice way to do this with reduce.
And there is:
(defun collect-maxima-by-third (list)
(reduce
#'(lambda (max-list next-element)
(let ((max-value (third (first max-list)))
(next-value (third next-element)))
(cond ((< max-value next-value)
(list next-element))
((= max-value next-value)
(cons next-element max-list))
(t max-list)))) ; the greater-than case
(rest list)
:initial-value (list (first list))))
It's not perfect, as if you give it an empty list it will give you a list containing an empty list instead of just an empty list, but you can easily add a case for this if you think that will happen often.
This type of technique (maybe not this exact example) is detailed in various texts on functional programming; some Haskell texts do a particularly good job (Learn You a Haskell comes to mind).

How do I print a list of numbers on each line in clojure?

how can I print a list of n, say 10, numbers on 10 lines? I just learned about loop and recur, but cannot seem to combine a side-effect (println i) with (recur (+ i 1)) in a loop form.
Just to be very clear: I'd like output like this:
1
2
3
4
5
6
7
8
9
10
when n is 10.
You can use doseq for this, which is meant to be used when iteration involves side effects,
(doseq [i (range 10)]
(println i))
You could use map as pointed but that will produce a sequence full of nils which is both not idiomatic and wastes resources also doseq is not lazy so no need to force it with doall.
I suggest dotimes for this kind of simple loop:
(dotimes [i 10]
(println (inc i)))
Note that dotimes is non-lazy, so it is good for things like println that cause side effects.
With loop/recur:
(loop [i 1]
(when (<= i 10)
(println i)
(recur (inc i))))
However, it's more idiomatic (read: more "Clojuristic") to map the function println over the numbers in 1..10. But because map returns a lazy sequence, you must force its evaluation with doall:
(doall (map println (range 1 (inc 10))))
And just to be comprehensive you can do it with map also:
(doseq (map #(println %) (range 10))
If you only want to print the output on the screen, you might also simply put a (println i) before entering your conditional:
(loop [i 0]
(println i)
(if (< i 10)
(recur (inc i))
(println "done!")))
And the output will be one number per line.

Creating an n-sized permutation with scheme using only basic constructs

Is it possible to generate n-sized permutations of a list using only the basic scheme constructs?
With define you can do it like this (without define the answer would be no, because you'll need to use recursion):
First define a function that takes a list of lists and a value and returns a list of lists where the given item has been prepended to each list in the original list of lists.
This can be done by writing a simple recursive function that uses cons to prepend the item to the first list (using car to get the first list) and then uses cons again to prepend the extended list to the result of calling the function on the other lists (i.e. on the cdr of the list of lists). If the list is empty (and thus doesn't have a car and cdr), return the empty list.
You'll also need a function that removes a given item from a list. This can also be done by defining a simple recursive function that takes an item and a list. At each step the `car´ of the given list should be prepended to the result of the recursive call if it is not equal to the item that is to be deleted. If it is equal, the result of the recursive call should be returned directly.
Further you'll need a function to concatenate lists. This can also be implemented recursively without too much trouble.
Then define a function that given a list of lists and an item calls the previous function with the item and each sublist as its argument.
Now define the a function that creates n-sized permutations. This function should take the number n and a list. If n is 0, it should return the empty list. Otherwise it should call itself recursively for each item x in the list with (- n 1) as the new value for n and the result of removing x from the list as the new value for the list. Then the results of the recursive calls should be concatenated.
This is an explanation of the code found in Rosetta, although, I have changed the variable names to help make it more readable, and added my explanation of the code below. I did check to see if the code works in DrRacket, and it does.
Before defining permute, two helper functions are required namely, seq and insert.
seq builds a list containing a sequence of numbers. For example (seq 0 3) -> (0 1 2 3).
The elements (numbers) in the list are used in the insert function to insert the carItem at various positions in the 'cdr' list.
(define (seq start end)
(if (= start end)
(list end) ; if start and end are the same number, we are done
(cons start (seq (+ start 1) end))
)
)
insert generates a list with the carItem inserted in the "n"th position of the cdrList. For example, (insert '(b c) 0 'a) -> '(a b c) and (insert '(b c) 2 'a) -> '(b c a).
(define (insert cdrList n carItem)
(if (= 0 n)
(cons carItem cdrList) ; if n is 0, prepend carItem to cdrList
(cons (car cdrList)
(insert (cdr cdrList) (- n 1) carItem))))
Finally, as for the main function permute, it uses insert and seq in a recursive manner.
For example, when plist = '(b,c) the lambda evals to the following:
; (map (lambda (n)
; (insert '(b c) n 'a))
; '(0 1 2)) -> output of seq function given n = 2, which is length of '(b c)
; '((a b c) (b a c) (b c a)) ---> will be the output
(define (permute mylist)
(if (null? mylist)
'(())
(apply append (map (lambda (plist)
(map (lambda (n)
(insert plist n (car mylist)))
(seq 0 (length plist))))
(permute (cdr mylist))))))
(permute '(a b c))
If the above nested lambdas makes your head spin (it did for me), find below, IMHO, a more readable "define" version, thanks to Matthias Felleisen:
(define (permute mylist)
(cond
[(null? mylist) '(())]
[else
(define (genCombinationsFor plist)
(define (combineAt n) (insert plist n (car mylist)))
(map combineAt (seq 0 (length plist))))
(apply append (map genCombinationsFor (permute (cdr mylist))))]))

Resources