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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With