There is a reason for not making (pop aList -1) as simple as (push elem aList -1): it is performance, which degrades for longer lists, because there is a pointer forwards from each cell to its next cell in the list, but not backwards. This has implications for pop back (because its predecessor has to be reached from the other list end), but not for push back: thanks to 'last element optimization' of keeping a pointer to last element in list.
So there is an asymmetry in the sense, that regarding performance it's better to leave the FIFO/LIFO choice at pushers side, which is visible in making (or leaving) the less performant variants more difficult to use.
Nevertheless there may be usecases for the cpop/em-cpop macros (see link in previous update).
[Update] See viewtopic.php?f=16&t=4732#p23319 for better and more general macro code.
Starting point
By coding something with FIFOs and LIFOs, I've seen some 'asymmetries' between push and pop, which led to a macro repairing them. Not really a serious problem (thanks to the flexibility of newLISP), but nevertheless this possibly could be better...
Differences between push and pop
There are big differences between push and pop:
- On one side push is robust, if pushing into s symbol set to nil: it creates an empty list then, and ignores some given index just in this special case.
- On the other side pop always expects a list, no one will be created, if there is none (together with ignoring a given ix then).
More symmetry
- Some more symmetry between push and pop could be reached by the following:
define both- (pop '() -1)) and (pop '() 0)
as valid expression returning nil; this corresponds to - (push "foo" '() 0) and (push "foo" '() -1)
, which is working now.
- (pop '() -1)) and (pop '() 0)
- More more symmetry would be reached by creating an empty list for pop'ing from a nil symbol value, just as for push (ignoring any given ix and returning nil in this case).
Code: Select all
(push '() 1)
Postfix:
It is clear, that changing the semantics of push and/or pop could break some legacy code; so such a change would likely deserve a greater version number increase.
How I've come to these suggestions
There has been a need for macro
Code: Select all
(macro (pop-bottom-repaired L) (if (> (length L) 0) (pop L -1)))
After calling newlisp with this code:
Code: Select all
;; define some things
(macro (push-top E L) (push E L ))
(macro (push-bottom E L) (push E L -1))
(macro (pop-top L) (pop L ))
(macro (pop-bottom L) (pop L -1))
;; repair by:
(macro (pop-bottom-repaired L) (if (> (length L) 0) (pop L -1)))
(define (doIt num_push push_op_sym num_pop pop_op_sym)
(print (format "%45s" (string "push_op: " push_op_sym ", pop_op: " pop_op_sym)))
(set 'l '()
'push_op (eval push_op_sym)
'pop_op (eval pop_op_sym))
(dolist (e (sequence 1 num_push))
(eval (push_op e l)))
(print "; after push_op: " l)
(print "; pop:")
(dotimes (i num_pop)
(print " " (eval (pop_op l))))
(println))
Code: Select all
newLISP v.10.6.4 64-bit on OSX IPv4/6 UTF-8 libffi, options: newlisp -h
> ;;
> ;; this works:
> ;;
> (doIt 3 'push-top 3 'pop-top) ; LIFO
push_op: push-top, pop_op: pop-top; after push_op: (3 2 1); pop: 3 2 1
nil
> (doIt 3 'push-bottom 3 'pop-bottom) ; LIFO
push_op: push-bottom, pop_op: pop-bottom; after push_op: (1 2 3); pop: 3 2 1
nil
> (doIt 3 'push-bottom 3 'pop-top) ; FIFO
push_op: push-bottom, pop_op: pop-top; after push_op: (1 2 3); pop: 1 2 3
nil
> (doIt 3 'push-top 3 'pop-bottom) ; FIFO
push_op: push-top, pop_op: pop-bottom; after push_op: (3 2 1); pop: 1 2 3
nil
> ;;
> ;; but this fails, if 'pop-bottom is trying to pop from an empty list:
> ;;
> (doIt 3 'push-top 4 'pop-top) ; LIFO
push_op: push-top, pop_op: pop-top; after push_op: (3 2 1); pop: 3 2 1 nil
nil
> (doIt 3 'push-bottom 4 'pop-bottom) ; LIFO
push_op: push-bottom, pop_op: pop-bottom; after push_op: (1 2 3); pop: 3 2 1
ERR: invalid list index in function pop
called from user function (doIt 3 'push-bottom 4 'pop-bottom)
> (doIt 3 'push-bottom 4 'pop-top) ; FIFO
push_op: push-bottom, pop_op: pop-top; after push_op: (1 2 3); pop: 1 2 3 nil
nil
> (doIt 3 'push-top 4 'pop-bottom) ; FIFO
push_op: push-top, pop_op: pop-bottom; after push_op: (3 2 1); pop: 1 2 3
ERR: invalid list index in function pop
called from user function (doIt 3 'push-top 4 'pop-bottom)
> ;;
> ;; using repaired version: voila!
> ;;
> (doIt 3 'push-top 4 'pop-top) ; LIFO
push_op: push-top, pop_op: pop-top; after push_op: (3 2 1); pop: 3 2 1 nil
nil
> (doIt 3 'push-bottom 4 'pop-bottom-repaired) ; LIFO
push_op: push-bottom, pop_op: pop-bottom-repaired; after push_op: (1 2 3); pop: 3 2 1 nil
nil
> (doIt 3 'push-bottom 4 'pop-top) ; FIFO
push_op: push-bottom, pop_op: pop-top; after push_op: (1 2 3); pop: 1 2 3 nil
nil
> (doIt 3 'push-top 4 'pop-bottom-repaired) ; FIFO
push_op: push-top, pop_op: pop-bottom-repaired; after push_op: (3 2 1); pop: 1 2 3 nil
nil
>
Code: Select all
;;
;; this works:
;;
(doIt 3 'push-top 3 'pop-top) ; LIFO
(doIt 3 'push-bottom 3 'pop-bottom) ; LIFO
(doIt 3 'push-bottom 3 'pop-top) ; FIFO
(doIt 3 'push-top 3 'pop-bottom) ; FIFO
;;
;; but this fails, if 'pop-bottom is trying to pop from an empty list:
;;
(doIt 3 'push-top 4 'pop-top) ; LIFO
(doIt 3 'push-bottom 4 'pop-bottom) ; LIFO
(doIt 3 'push-bottom 4 'pop-top) ; FIFO
(doIt 3 'push-top 4 'pop-bottom) ; FIFO
;;
;; using repaired version: voila!
;;
(doIt 3 'push-top 4 'pop-top) ; LIFO
(doIt 3 'push-bottom 4 'pop-bottom-repaired) ; LIFO
(doIt 3 'push-bottom 4 'pop-top) ; FIFO
(doIt 3 'push-top 4 'pop-bottom-repaired) ; FIFO