Understanding function `tailp` in Common Lisp - lisp

While looking through the "Common Lisp Quick Reference" by Bert Burgemeister, I stumbled over tailp.
First, I misunderstood the definitions of this function. And I tried:
(tailp '(3 4 5) '(1 2 3 4 5))
But it returned
NIL
CLTL2 says, tailp is true iff the first argument is any (nthcdr n list) with existing n.
(nthcdr 2 '(1 2 3 4 5))
;; (3 4 5)
I further tried:
(tailp '(3 4 5) '(1 2 3 4 5))
;; NIL - and I would expect: T following the definition above.
(tailp '() '(1 2 3 4 5))
;; T
(tailp '5 '(1 2 3 4 . 5))
;; T
Until I tried (and then understood tailp looks for cdr of l which share even the same address):
(defparameter l '(1 2 3 4 5 6))
(tailp (nthcdr 3 l) l)
;; T
But then I had my next question:
For what such a function is useful at all?
Wouldn't be a function more useful which looks whether a sublist is part of a list? (Or looks like a part of a list, instead that it has to share the same address?)
Remark:
Ah okay slowly I begin to understand, that maybe that this is kind of a eq for cdr parts of a list ... Kind of ... "Any cdr-derivative of given list eq to the first argument?".
But maybe someone can explain me in which situations such test is very useful?
Remark:
In a long discussion with #Lassi here, we found out:
Never Use tailp On Circular Lists!
Because the behavior is undefined (already in SBCL problematic).
So tailp is for usage on non-circular lists.

The basic purpose of tailp is to check whether there is list structure shared. This means whether the cons cells are the same (which means EQL as a predicate) - not just the content of the cons cells.
One can also check if an item is in the last cdr:
CL-USER 87 > (tailp t '(1 2 3 4 . t))
T
CL-USER 88 > (tailp nil '(1 2 3 4 . nil))
T
CL-USER 89 > (tailp nil '(1 2 3 4))
T
CL-USER 90 > (tailp #1="e" '(1 2 3 4 . #1#))
T
This is one of the rarely used functions in Common Lisp.

Here is a case where tailp is useful:
(defun circular-list-p (l)
(and (consp l)
(tailp l (rest l))))
A couple of notes.
This terminates in all cases: tailp is allowed not to terminate on circular lists if the first argument is not a tail of the second (ie there's no requirement for it to check circularity), but it must terminate if the first argument is a tail of the second. But, if the list is circular, that's exactly what we check for here, so it terminates. (I was confused about this for a while).
The consp check is so (circular-list-p nil) is false: I think this is pragmatically the useful choice although you a pedant might argue that nil is circular.

I'm pretty sure the answer to (tailp '(3 4 5) '(1 2 3 4 5)) can be both t and nil as a smart compiler might do (tailp '#1=#(3 4 5) '(1 2 . #1#)) to reduce memory footprint. Quoted stuff are immutable literals so why not use the same memory twice?
Here is how tailp is working:
(defparameter *tail* (list 3 4 5))
(defparameter *larger* (list* 1 2 *tail*))
(defparameter *replica* (copy-list *larger*))
(tailp *tail* *replica*) ; ==> nil
(tailp *tail* *larger*) ; ==> t
Since copy-list creates new cons for each element in a list it will share
nothing but the empty list with any other list. It is unique.
*larger* has been made with a common tail with *tail* and thus (tailp *tail* *larger*) will be t.
It's important that it compares the arguments as same objects. Since the tail does not need to be a list it compares with eql. When comparing if stuff look the same you use equal so tailp is more specific than that. It has to be pointer equal (eq) or eql atomic value.
So where do you use this? I'm thinking a functional data structure where you typically reuse shared structure. tailp might be used to identify a subtree of a parent. It's basically a more performant version of this:
(defun my-tailp (needle haystack)
(cond ((eql needle haystack) t)
((atom haystack) nil)
(t (my-tailp needle (cdr haystack)))))

#Sylwester:
Thank you, #Sylwester!
I recently read in Edi Weitz's book about tail wagging a list:
(defparameter *list* (list 'a 'b 'c 'd))
(defparameter *tail* (cdddr *list*))
This names the car of the last cons cell of the list as *tail* - and now, one can add to it a new element and rename the new last car of the last cons cell of the list *tail*.
(setf (cdr *tail*) (cons 'e 'nil)
*tail* (cdr *tail*))
;; (E)
Now the list is:
*list*
;; (A B C D E)
and one can add via setf further things to *tail* without having to traverse the list again. (So improves performance. But with a warning of course, because this is a destructive action).
Perhaps, if one would name a list like you did:
(defparameter *my-new-tail* '(F G H))
and tail wagg it to the end of the new list
(setf (cdr *tail*) *my-new-tail*
*tail* (cddr *my-new-tail*))
Ah or alternatively:
(defparameter *tail-of-my-new-tail* (cddr *my-new-tail*))
;; and then
(setf (cdr *tail*) *my-new-tail*
*tail* *tail-of-my-new-tail*)
Then
(tailp *my-new-tail* *list*)
would be the test, whether this procedure was done correctly
would also be a test, whether I added a new tail in addition to *my-new-tail* or not or if *my-new-tail* was the last tail which has been tail wagged to *list* or not ...
therefore tailp would be a quite useful test in the context of tail wagging ...
or like you said, if an implementation uses then tail wagging for performance reasons, (and perhaps keeping track of the tails of the lists constantly) in this context, the implementation could use tailp as a test whether a given list contributes to another list as the recently added tail ...
This just came into my thoughts, when reading your answer, #Sylwester! (I didn't realize this while reading about tail wagging in the book - (which is by the way a super useful one!) Thank you that you answered!

Related

Why does calling my home-rolled (reduce) function with a macro affect future calls to the function?

Background: I was working on a project in guile 1.8.8 scheme a few weeks ago and, being a little rusty, I forgot about the built-in (reduce) function, so I rolled my own. A little later, I ran into what seemed to be a hopeless bug, where calling a function with no side effects changed the flow of the rest of the program (working properly and passing a few unit tests vs. crashing completely) AFTER the function had long since returned. Several pieces of code which used to return something like (A B C D) were now returning only (A), causing a multitude of problems.
Minimum working example: After several days of whittling, I cornered the problem into this small piece of stand-alone code:
(define (my-reduce fun ls)
(if (null? (cdr ls))
(car ls)
(my-reduce fun (cons (fun (car ls) (cadr ls))
(cddr ls)))))
(format #t "~a " (my-reduce + '(1 2 3)))
(format #t "~a " (my-reduce or '(1 2 3)))
(format #t "~a~%" (my-reduce + '(1 2 3)))
Which prints out 6 1 1, instead of the expected 6 1 6.
Additional observations:
Setting the second line to a + yields the expected 6 6 6.
Setting the second line to and yields 6 3 3.
Additional lines of + after these produce additional 1s or 3s depending on what the second line is set to. So, the sequence + or + + leads to the output 6 1 1 1.
Additional lines of and or or after the first do NOT switch the output back. So, if the sequence is + and or +, the output is 6 3 3 3. It seems as though, once I've passed or or and to (my-reduce), the function becomes permanently "stuck" having that as its argument.
I also notice that passing and or or to the built-in (reduce) function causes a type-error, since they are technically macros and not functions.
Along those same lines, I notice that swapping out or for (lambda (x y) (or x y)) yields the expected output. Therefore, it seems that the critical thing here is that passing macros to my home-rolled reduce function causes an issue.
Question: What is going on here? Why does calling (my-reduce) with and or or cause such unexpected behavior? Does it have to do with the fact that those "functions" are actually macros?
Thanks in advance for any help. This one has really stumped me!
You said it yourself, you can't pass and, or as parameters where a function is expected because they're macros, and will complain with a "bad syntax" error. In fact, I can't understand how this even works for you:
(my-reduce or '(1 2 3))
The only way I can think of is that you redefined and, or as functions somewhere, and that is the source of the problems. As a side note, my-reduce (understood as a fold-right operation) can be implemented in a more standard way like this:
(define (my-reduce fun ls)
(if (null? (cdr ls))
(car ls)
(fun (car ls)
(my-reduce fun (cdr ls)))))
The above assumes that the list is non-empty, if that's not always the case, the usual is to pass as parameter an initial value:
(define (my-reduce fun init ls)
(if (null? ls)
init
(fun (car ls)
(my-reduce fun init (cdr ls)))))

Why is (type-of list) equal to CONS?

I am playing around with Common Lisp and just realized that
(type-of (cons 1 2)) is CONS
and
(type-of (list 1 2)) is also CONS
However the two are clearly not the same because all "proper" lists, must be conses with second element being a list.
That said, when there are only two elements, the second element is 2, and first element is 1, neither is a list, but the construct is also still called a cons.
This gets even more confusing since
(print (list (cons 1 2) 3)) ; this is a ((1 . 2) 3), an improper list, but still cons
(print (cons 1 (list 2 3))) ; this is a (1 2 3), a proper list, but still cons
(cons 1 (cons 2 3)) ; is not a proper list, but is a (1 2 . 3), but still cons...
All are cons, but why isn't (list 1 2) a list? It can't be a cons because cons and list must be different types in order to be told apart in the algorithm for determining whether or not it is a proper list (and in turn, (equal (list 1 2) (cons 1 2)) should be true; without this discrimination, there should be no difference between a cons and a list, there would just be a cons.
Can somebody please help me understand why it says that (type-of (list 1 2)) is cons, even though it is clearly a list (otherwise it would be an improper list to my understanding).
Proper and improper lists are not defined at the type level. This would require recursive type definitions which is only possible to do with Lisp with a satisfies type, and in that case type-of would still not return a type-specifier as complex:
b. the type returned does not involve and, eql,
member, not, or, satisfies or values.
The list type could be defined as (or cons null):
The types cons and null form an exhaustive partition of the type list.
That means that nil is a list, and any cons cell is a list. See also the definition of listp.
In other words:
(typep '(a b c) 'cons)
=> T
But also:
(typep '(a b c) 'list)
=> T
And of course this is true for any supertype:
(typep '(a b c) 'sequence)
=> T
(typep '(a b c) 't)
=> T
The type-of function returns the most basic type, i.e. cons, which can be thought of as the type for which no other subtype satisfy typep (but read the specification which gives the actual definition).
Remarks
Just to clarify:
(cons 1 2)
... is a list, but it cannot be passed to functions that expect proper lists like map, etc. This is checked at runtime and generally, there is no confusion because the cases where one use improper lists are actually quite rare (when you treat cons cells as trees, for example). Likewise, circular lists require special treatment.
In order to check if a list is proper or not, you only need to check whether the last cons has a nil or not as its cdr.
Also, I saw that you wrote:
((1 . 2) 3) ; [...] an improper list
What you have here is a proper-list of two elements where the first one is an improper list, a.k.a. a dotted-list.
#coredump's answer is the correct one, but it's perhaps useful to see pragmatic reasons why it's correct.
Firstly, it's quite desirable that typechecks are quick. So if I say (typep x 'list), I'd like it not to have to go away for a long time to do the check.
Well, consider what a proper list checker has to look like. Something like this, perhaps:
(defun proper-list-p (x)
(typecase x
(null t)
(cons (proper-list-p (rest x)))
(t nil)))
For any good CL compiler this is a loop (and it can obviously be rewritten as an explicit loop if you might need to deal with rudimentary compilers). But it's a loop which is as long as the list you are checking, and this fails the 'typechecks should be quick' test.
In fact it fails a more serious test: typechecks should terminate. Consider a call like (proper-list-p #1=(1 . #1#)). Oops. So we need something like this, perhaps:
(defun proper-list-p (x)
(labels ((plp (thing seen)
(typecase thing
(null (values t nil))
(cons
(if (member thing seen)
(values nil t) ;or t t?
(plp (rest thing)
(cons thing seen))))
(t (values nil nil)))))
(plp x '())))
Well, this will terminate (and tell you whether the list is circular):
> (proper-list-p '#1=(1 . #1#))
nil
t
(This version considers circular lists not to be proper: I think the other decision is less useful but perhaps equally justified in some theoretical sense.)
But this is now quadratic in the length of the list. This can be made better by using a hashtable in the obvious way, but then the implementation is ludicrously consy for small lists (hashtables are big).
Another reason is to consider the difference between representational type and intentional type: the representational type of something tells you how it is implemented, while the intentional type tells you what it logically is. And it's easy to see that, in a lisp with mutable data structures, it is absurdly difficult for the representational type of a (non-null) list to be different than that of a cons. Here's an example of why:
(defun make-list/last (length init)
;; return a list of length LENGTH, with each element being INIT,
;; and its last cons.
(labels ((mlt (n list last)
(cond ((zerop n)
(values list last))
((null last)
(let ((c (cons init nil)))
(mlt (- n 1) c c)))
(t (mlt (- n 1) (cons init list) last)))))
(mlt length '() '())))
(multiple-value-bind (list last) (make-list/last 10 3)
(values
(proper-list-p list)
(progn
(setf (cdr last) t)
(proper-list-p list))
(progn
(setf (cdr (cdr list)) '(2 3))
(proper-list-p list))))
So the result of the last form is t nil t: list is initially a proper list, then it isn't because I fiddled with its final cons, then it is again because I fiddled with some intermediate cons (and now, whatever I do to the cons bound to last will make no difference to that bound to list).
It would be insanely difficult to keep track, in terms of representational type, of whether something is a proper list or not, if you want to use anything that is remotely like linked lists. And type-of, for instance, tells you the representational type of something, which can only be cons (or null for empty lists).

push does not work as I would expect it - why?

I am experiencing a behavior of the push function that I don't get. Maybe someone could explain to me why Lisp behaves this way.
Supposed I define a list as a global variable, and then try to push a new value to it using the following code:
(defparameter *primes* '(3 5 7 11))
(push 2 *primes*)
Then *primes* is now (2 3 5 7 11). So far, so good.
Now I try to do the same thing, but without the *primes* variable, i.e.:
(push 2 '(3 5 7 11))
The result is an error message:
EVAL: 3 is not a function name; try using a symbol instead
Now I have two questions:
Why does this not work? I would expect that push returns the list (2 3 5 7 11), why does this not happen? Where am I wrong?
Apart from that, I don't get the error message. What is Lisp trying to tell me with 3 is not a function name? Of course, 3 is not a function name, but I don't try to call a function named 3 anywhere, do I?
Any help is appreciated :-)
If you read the CL Hyperspec for PUSH, you will read that push expects a place.
A place is something like a variable, a structure slot, a class slot, an array access, or similar. Since Lisp uses linked cons cells for lists, it does not make sense to push something in front of a cons cell, without a reference for that.
So above is simple: we can't push to a direct list.
Why this error message?
This gets a bit complicated...
(push 2 '(3 5 7 11))
Is actually:
(push 2 (quote (3 5 7 11))
A function can be a place, it then needs a corresponding setter function. Here the setter is thought to be (setf quote) - that's right, Common Lisp can sometimes have lists as function names, not only symbols.
If we look at the macroexpansion of above:
? (pprint (macroexpand '(push 2 (quote (3 5 7 11)))))
(LET* ((#:G328 2) (#:G327 (3 5 7 11)) (#:G326 (CONS #:G328 '#:G327)))
#:G327
#:G326
(FUNCALL #'(SETF QUOTE) #:G326 #:G327))
You can see that it tries to call the setter. But it also thinks that (3 5 7 11) is a Lisp form.
I give you an example, where it actually works, but we don't use quote, but a real accessor function:
CL-USER 40 > (let ((v (vector (list (list 'a 'b 'c) (list 'd 'e 'f))
(list (list 1 2 3) (list 4 5 6)))))
(print v)
(push 42 (first (aref v 1)))
(print v)
(values))
#(((A B C) (D E F)) ((1 2 3) (4 5 6)))
#(((A B C) (D E F)) ((42 1 2 3) (4 5 6)))
In above first is the getter and CL knows the corresponding setter. The form (aref v 1) is the call and returns the index 1 element of the vector. We are then pushing to the first list of the element.
Your call has a similar structure and (3 5 7 11) is at a similar position as (aref v 1). The Lisp system says that in (3 4 7 11) then number 3 is not a valid function. Which is correct. But the real error was about the push operation. Since the macro could not detect the error, the error gets later detected in the macro expanded code.
I have found only the emacs lisp manual push, but I guess it behaves similar for Common Lisp
— Macro: push element listname
This macro creates a new list whose car is element and whose cdr is the list specified by listname, and saves that list in listname.
So it seems push is modifying its argument listname, which isn't possible with a literal list. To do what you have in mind, one would use cons instead.
To the second part 3 is not a function name, I would say push, or some function inside it, tries to evaluate the literal list. Evaluating (3 5 7 11) means, call the function 3 with arguments 5 7 11. Hence the error message.
Again from emacs, Ctrl-h f push
push is a Lisp macro in `cl.el'.
(push X PLACE)
Insert X at the head of the list stored in PLACE.
Analogous to (setf PLACE (cons X PLACE)), though more careful about
evaluating each argument only once and in the right order. PLACE may
be a symbol, or any generalized variable allowed by `setf'.
setf in turn allows place to be a
symbolic references such as (car x) or (aref x i)
which explains, why push evaluates the second argument.
I think you need CONS in second case:
(cons 2 '(3 5 7 11)) => (2 3 5 7 11)

Searching for a integer in a list (Lisp)

I cannot think of a way to search a list to make sure it has all integers. I want to immediately return nil if there is non-integer data, and continue my function if there is not.
The recursive function I am trying to make will cons the car with the cdr of the list. With the attempts I have made, I am not able to return nil. I have only been able to ignore the non-integer data. E.g., (add-1-all '(1 2 3 a)) will return (2 3 4) after adding one to each number.
(defun add-1-all (L)
(if (null L)
L
(if (integerp (car L))
(cons (+ (car L) 1) (add-1-all (cdr L)))
nil)))
I do understand that the cons is making this happen, as the recursion is adding on to the list.
Your first sentence,
I cannot think of a way to search a list in Lisp to make sure it has all integers.
makes it sound like you want to check whether a list is all integers. You can check whether a list is all integers using every:
CL-USER> (every 'integerp '(1 2 3 4))
;=> T
CL-USER> (every 'integerp '(1 2 a 4))
;=> NIL
every will take care of short-circuiting, i.e., returning nil as soon as the first element failing the predicate is found.
However, your code makes it sound like you want to map over a list, collecting the value of a function applied to each integer and returning the collected values, except that if you encounter a non-integer, you return null. Perhaps the easiest way to do this is using the loop macro. A solution looks almost identical to the English specification:
CL-USER> (loop for x in '(1 2 3 4)
if (not (integerp x)) return nil
else collect (1+ x))
;=> (2 3 4 5)
CL-USER> (loop for x in '(1 2 a 4)
if (not (integerp x)) return nil
else collect (1+ x))
;=> NIL
Doing this with loop also has some advantages over a recursive solution. While some languages in the Lisp family (e.g., Schema) require tail call optimization, and some Common Lisp implementations do it too, it's not required in Common Lisp. As a result, it can be safer (e.g., you won't run out of stack space) if you use an iterative solution (e.g., with loop) rather than a recursive (even a tail-recursive) implementation.
The key is to use a helper function, and for that helper function to carry the answer along with it as it recurses, so that it can discard the whole thing at any time if necessary. Incidentally, this will also be tail recursive, meaning that it can deal with extremely long lists without running out of stack space.
(defun add-1-all (L)
(add-1-all-helper L nil))
(defun add-1-all-helper (L answer)
(if (null L)
answer
(if (integerp (car L))
(add-1-all-helper
(cdr L)
(cons (+ (car L) 1) answer)))))

How do I write Push and Pop in Scheme?

Right now I have
(define (push x a-list)
(set! a-list (cons a-list x)))
(define (pop a-list)
(let ((result (first a-list)))
(set! a-list (rest a-list))
result))
But I get this result:
Welcome to DrScheme, version 4.2 [3m].
Language: Module; memory limit: 256 megabytes.
> (define my-list (list 1 2 3))
> (push 4 my-list)
> my-list
(1 2 3)
> (pop my-list)
1
> my-list
(1 2 3)
What am I doing wrong? Is there a better way to write push so that the element is added at the end and pop so that the element gets deleted from the first?
This is a point about using mutation in your code: there is no need to jump to macros for that. I'll assume the stack operations for now: to get a simple value that you can pass around and mutate, all you need is a wrapper around the list and the rest of your code stays the same (well, with the minor change that makes it do stack operations properly). In PLT Scheme this is exactly what boxes are for:
(define (push x a-list)
(set-box! a-list (cons x (unbox a-list))))
(define (pop a-list)
(let ((result (first (unbox a-list))))
(set-box! a-list (rest (unbox a-list)))
result))
Note also that you can use begin0 instead of the let:
(define (pop a-list)
(begin0 (first (unbox a-list))
(set-box! a-list (rest (unbox a-list)))))
As for turning it into a queue, you can use one of the above methods, but except for the last version that Jonas wrote, the solutions are very inefficient. For example, if you do what Sev suggests:
(set-box! queue (append (unbox queue) (list x)))
then this copies the whole queue -- which means that a loop that adds items to your queue will copy it all on each addition, generating a lot of garbage for the GC (think about appending a character to the end of a string in a loop). The "unknown (google)" solution modifies the list and adds a pointer at its end, so it avoids generating garbage to collect, but it's still inefficient.
The solution that Jonas wrote is the common way to do this -- keeping a pointer to the end of the list. However, if you want to do it in PLT Scheme, you will need to use mutable pairs: mcons, mcar, mcdr, set-mcar!, set-mcdr!. The usual pairs in PLT are immutable since version 4.0 came out.
You are just setting what is bound to the lexical variable a-list. This variable doesn't exist anymore after the function exits.
cons makes a new cons cell. A cons cell consists of two parts, which are called car and cdr. A list is a series of cons cells where each car holds some value, and each cdr points to the respective next cell, the last cdr pointing to nil. When you write (cons a-list x), this creates a new cons cell with a reference to a-list in the car, and x in the cdr, which is most likely not what you want.
push and pop are normally understood as symmetric operations. When you push something onto a list (functioning as a stack), then you expect to get it back when you pop this list directly afterwards. Since a list is always referenced to at its beginning, you want to push there, by doing (cons x a-list).
IANAS (I am not a Schemer), but I think that the easiest way to get what you want is to make push a macro (using define-syntax) that expands to (set! <lst> (cons <obj> <lst>)). Otherwise, you need to pass a reference to your list to the push function. Similar holds for pop. Passing a reference can be done by wrapping into another list.
Svante is correct, using macros is the idiomatic method.
Here is a method with no macros, but on the down side you can not use normal lists as queues.
Works with R5RS at least, should work in R6RS after importing correct libraries.
(define (push x queue)
(let loop ((l (car queue)))
(if (null? (cdr l))
(set-cdr! l (list x))
(loop (cdr l)))))
(define (pop queue)
(let ((tmp (car (car queue))))
(set-car! queue (cdr (car queue)))
tmp))
(define make-queue (lambda args (list args)))
(define q (make-queue 1 2 3))
(push 4 q)
q
; ((1 2 3 4))
(pop a)
; ((2 3 4))
q
I suppose you are trying to implement a queue. This can be done in several ways, but if you want both the insert and the remove operation to be performed in constant time, O(1), you must keep a reference to the front and the back of the queue.
You can keep these references in a cons cell or as in my example, wrapped in a closure.
The terminology push and pop are usually used when dealing with stacks, so I have changed these to enqueue and dequeue in the code below.
(define (make-queue)
(let ((front '())
(back '()))
(lambda (msg . obj)
(cond ((eq? msg 'empty?) (null? front))
((eq? msg 'enqueue!)
(if (null? front)
(begin
(set! front obj)
(set! back obj))
(begin
(set-cdr! back obj)
(set! back obj))))
((eq? msg 'dequeue!)
(begin
(let ((val (car front)))
(set! front (cdr front))
val)))
((eq? msg 'queue->list) front)))))
make-queue returns a procedure which wraps the state of the queue in the variables front and back. This procedure accepts different messages which will perform the procedures of the queue data structure.
This procedure can be used like this:
> (define q (make-queue))
> (q 'empty?)
#t
> (q 'enqueue! 4)
> (q 'empty?)
#f
> (q 'enqueue! 9)
> (q 'queue->list)
(4 9)
> (q 'dequeue!)
4
> (q 'queue->list)
(9)
This is almost object oriented programming in Scheme! You can think of front and back as private members of a queue class and the messages as methods.
The calling conventions is a bit backward but it is easy to wrap the queue in a nicer API:
(define (enqueue! queue x)
(queue 'enqueue! x))
(define (dequeue! queue)
(queue 'dequeue!))
(define (empty-queue? queue)
(queue 'empty?))
(define (queue->list queue)
(queue 'queue->list))
Edit:
As Eli points out, pairs are immutable by default in PLT Scheme, which means that there is no set-car! and set-cdr!. For the code to work in PLT Scheme you must use mutable pairs instead. In standard scheme (R4RS, R5RS or R6RS) the code should work unmodified.
What you're doing there is modifying the "queue" locally only, and so the result is not available outside of the definition's scope. This is resulted because, in scheme, everything is passed by value, not by reference. And Scheme structures are immutable.
(define queue '()) ;; globally set
(define (push item)
(set! queue (append queue (list item))))
(define (pop)
(if (null? queue)
'()
(let ((pop (car queue)))
(set! queue (cdr queue))
pop)))
;; some testing
(push 1)
queue
(push 2)
queue
(push 3)
queue
(pop)
queue
(pop)
queue
(pop)
The problem relies on the matter that, in Scheme, data and manipulation of it follows the no side-effect rule
So for a true queue, we would want the mutability, which we don't have. So we must try and circumvent it.
Since everything is passed by value in scheme, as opposed to by reference, things remain local and remain unchanged, no side-effects. Therefore, I chose to create a global queue, which is a way to circumvent this, by applying our changes to the structure globally, rather than pass anything in.
In any case, if you just need 1 queue, this method will work fine, although it's memory intensive, as you're creating a new object each time you modify the structure.
For better results, we can use a macro to automate the creation of the queue's.
The push and pop macros, which operate on lists, are found in many Lispy languages: Emacs Lisp, Gauche Scheme, Common Lisp, Chicken Scheme (in the miscmacros egg), Arc, etc.
Welcome to Racket v6.1.1.
> (define-syntax pop!
(syntax-rules ()
[(pop! xs)
(begin0 (car xs) (set! xs (cdr xs)))]))
> (define-syntax push!
(syntax-rules ()
[(push! item xs)
(set! xs (cons item xs))]))
> (define xs '(3 4 5 6))
> (define ys xs)
> (pop! xs)
3
> (pop! xs)
4
> (push! 9000 xs)
> xs
'(9000 5 6)
> ys ;; Note that this is unchanged.
'(3 4 5 6)
Note that this works even though lists are immutable in Racket. An item is "popped" from the list simply by adjusting a pointer.