Printing the next string in a list
6th August 2016
A friend asked me if it is possible in Lisp to write an operator saynextof which, when called, prints the next item in its list of arguments. So:
(saynextof '("fred" "jim" "sheila"))
should print "fred" the first time it is called, "jim" the second time, "sheila" the third time, "fred" the fourth time, and so on.
It should be possible to have multiple separate occurrences of the call; so:
(defun test () (dotimes (i 4) (saynextof '("fred" "jim" "sheila")) (saynextof '("alan" "bill"))))
should print: "fred", "alan", "jim", "bill", "sheila", "alan", "fred", "bill".
An approach using a macro
Here's my attempt at a solution, which uses a helper function saynextof-helper:
(defun saynextof-helper () (let ((n 0)) (lambda (list) (print (nth n list)) (setq n (mod (+ n 1) (length list))))))
This creates a closure, using n as a counter, and returns a function to print the nth string in a list.
The following macro creates the necessary code to call the helper function:
(defmacro saynextof (list) (let ((fun (saynextof-helper))) `(funcall ,fun ,list)))
Calling the test function given above produces the desired result:
CL-USER > (test) "fred" "alan" "jim" "bill" "sheila" "alan" "fred" "bill"
The question is: can this be solved without using a macro?
Here's an alternative solution avoiding the need for a macro. I realised that each separate occurrence of saynextof in the source needs to have its own counter; the solution is to store these in a hash table, keyed by the list passed to saynextof:
(defparameter *h* (make-hash-table)) (defun saynextof (list) (let ((n (or (gethash list *h*) (setf (gethash list *h*) 0)))) (print (nth n list)) (setf (gethash list *h*) (mod (+ n 1) (length list)))))
blog comments powered by Disqus