I have two vectors with fill-pointers. I need to merge these vectors and have as a result a new vector that still has a fill-pointer.
(defparameter *a* (make-array 3 :fill-pointer 3
:initial-contents '(1 3 5)))
(defparameter *b* (make-array 3 :fill-pointer 3
:initial-contents '(0 2 4)))
(type-of *a*)
;;=> (VECTOR T 6)
;; Pushing new elements works as intended.
(vector-push-extend 7 *a*)
(vector-push-extend 6 *b*)
;; Now we create a new vector by merging *a* and *b*.
(defparameter *c* (merge 'vector *a* *b* #'<))
;;=> #(0 1 2 3 4 5 6 7)
(type-of *c*)
;;=> (SIMPLE-VECTOR 8)
;; The type of this new vector does not allow pushing elements.
(vector-push-extend 8 *c*)
;; The value
;; #(0 1 2 3 4 5 6 7)
;; is not of type
;; (AND VECTOR (NOT SIMPLE-ARRAY))
;; [Condition of type TYPE-ERROR]
I can't seem to find a type to specify to merge so that the result will have a fill-pointer. I guess the obvious workarounds would be either:
Write a merge function myself that declares a new vector and performs the insertions in the correct order.
Copy the result into another vector with a fill-pointer.
Of course both workarounds are pretty unsatisfactory if there is a way to do this using merge from the standard.
Indeed there is no easy way to get merge
return a vector with a
fill-pointer.
You can, however, displace your vector to the result:
(defparameter *c* (merge '(vector t) *a* *b* #'<))
(type-of *c*)
==> (SIMPLE-VECTOR 8)
(defparameter *d* (make-array (length *c*) :displaced-to *c* :fill-pointer t))
(type-of *d*)
==> (VECTOR T 8)
*d*
==> #(0 1 2 3 4 5 6 7)
(array-displacement *d*)
==> #(0 1 2 3 4 5 6 7); 0
(vector-push-extend 17 *d*)
==> 8
*d*
==> #(0 1 2 3 4 5 6 7 17)
So far so good, right?
Nope, not so fast:
(array-displacement *d*)
==> NIL; 0
When we called vector-push-extend
on *d*, it was converted from a displaced array to a normal one
because the underlying simple-vector
cannot be extended.
You might actually consider using lists instead of arrays if you want to
use merge because it is much more efficient on lists (reuses the
structure).
Related
According to the book, this is what the function definition is,
The function scramble takes a non-empty tuple in which no argument is greater than its own index and returns a tuple of same length. Each number in the argument is treated as a backward index from its own position to a point earlier in tuple. The result at each position is obtained by counting backward from the current position according to this index.
And these are some examples,
; Examples of scramble
(scramble '(1 1 1 3 4 2 1 1 9 2)) ; '(1 1 1 1 1 4 1 1 1 9)
(scramble '(1 2 3 4 5 6 7 8 9)) ; '(1 1 1 1 1 1 1 1 1)
(scramble '(1 2 3 1 2 3 4 1 8 2 10)) ; '(1 1 1 1 1 1 1 1 2 8 2)
Here is the implementation,
(define pick
(λ (i lat)
(cond
((eq? i 1) (car lat))
(else (pick (sub1 i)
(cdr lat))))))
(define scramble-b
(lambda (tup rev-pre)
(cond
((null? tup) '())
(else
(cons (pick (car tup) (cons (car tup) rev-pre))
(scramble-b (cdr tup)
(cons (car tup) rev-pre)))))))
(define scramble
(lambda (tup)
(scramble-b tup '())))
This is a case where using a very minimal version of the language means that the code is verbose enough that understanding the algorithm is not perhaps easy.
One way of dealing with this problem is to write the program in a much richer language, and then work out how the algorithm, which is now obvious, is implemented in the minimal version. Let's pick Racket as the rich language.
Racket has a function (as does Scheme) called list-ref: (list-ref l i) returns the ith element of l, zero-based.
It also has a nice notion of 'sequences' which are pretty much 'things you can iterate over' and a bunch of constructs whose names begin with for for iterating over sequences. There are two functions which make sequences we care about:
in-naturals makes an infinite sequence of the natural numbers, which by default starts from 0, but (in-naturals n) starts from n.
in-list makes a sequence from a list (a list is already a sequence in fact, but in-list makes things clearer and there are rumours also faster).
And the iteration construct we care about is for/list which iterates over some sequences and collects the result from its body into a list.
Given these, then the algorithm is almost trivial: we want to iterate along the list, keeping track of the current index and then do the appropriate subtraction to pick a value further back along the list. The only non-trivial bit is dealing with zero- vs one-based indexing.
(define (scramble l)
(for/list ([index (in-naturals)]
[element (in-list l)])
(list-ref l (+ (- index element) 1))))
And in fact if we cause in-naturals to count from 1 we can avoid the awkward adding-1:
(define (scramble l)
(for/list ([index (in-naturals 1)]
(element (in-list l)))
(list-ref l (- index element))))
Now looking at this code, even if you don't know Racket, the algorithm is very clear, and you can check it gives the answers in the book:
> (scramble '(1 1 1 3 4 2 1 1 9 2))
'(1 1 1 1 1 4 1 1 1 9)
Now it remains to work out how the code in the book implements the same algorithm. That's fiddly, but once you know what the algorithm is it should be straightforward.
If the verbal description looks vague and hard to follow, we can try following the code itself, turning it into a more visual pseudocode as we go:
pick i [x, ...ys] =
case i {
1 --> x ;
pick (i-1) ys }
==>
pick i xs = nth1 i xs
(* 1 <= i <= |xs| *)
scramble xs =
scramble2 xs []
scramble2 xs revPre =
case xs {
[] --> [] ;
[x, ...ys] -->
[ pick x [x, ...revPre],
...scramble2 ys
[x, ...revPre]] }
Thus,
scramble [x,y,z,w, ...]
=
[ nth1 x [x] (*x=1..1*)
, nth1 y [y,x] (*y=1..2*)
, nth1 z [z,y,x] (*z=1..3*)
, nth1 w [w,z,y,x] (*w=1..4*)
, ... ]
Thus each element in the input list is used as an index into the reversed prefix of that list, up to and including that element. In other words, an index into the prefix while counting backwards, i.e. from the element to the left, i.e. towards the list's start.
So we have now visualized what the code is doing, and have also discovered requirements for its input list's elements.
Why is it not possible to push directly on a list like '(1 2 3) or NIL?
Specifically:
Why is possible to do
> (let ((some-list nil))
(push 42 some-list))
(42)
but not to do something like
(push 42 nil)
or
(push 42 '(1 2 3))
What is the reasoning behind this implementation?
With macro push the second argument needs to be a place to be modified. Here are some examples:
Lets make two variables:
(defparameter *v* (list 2 4))
(defparameter *v-copy* *v*)
Then we push 0
(push 1 *v*) ; ==> (1 2 4)
*v-copy* ; ==> (2 4) (unaltered)
; the reason is that the variable is changed, not its value
(macroexpand '(push 1 v))
; ==> (setq v (cons 1 v))
push can use other things as second argument. Lets try a cons
(push 3 (cdr *v-copy*))
*v-copy* ; ==> (2 3 4)
; since the tail of *v* is the *v-copy* *v* is changed too
*v* ; ==> (1 2 3 4)
(macroexpand-1 '(push 2 (cdr *v-copy*)))
; ==> (rplacd v (cons 2 (cdr *v-copy*)))
If your examples were valid, what should it really have done? Lets do the nil first:
(macroexpand '(push 42 nil))
; ==> (setq nil (cons 42 nil))
This treats nil just as any other variable and if this worked nil would never be the empty list again. It would have been a list with one element, 42 and a different value than (). In Common Lisp nil is a constant and cannot be mutated. I've created a lisp once where nil was a variable like any other and a small typo redefined nil making the programs behave strange with no apparent reason.
Lets try your literal quoted list.
(macroexpand '(push 42 (quote (1 2 3))))
; ==> (let ((tmp (1 2 3)))
; (funcall #'(setf quote) (cons 42 'tmp) tmp))
It doesn't seem the push macro differentiates between special form quote and those types that has set their setf function. It won't work and it doesn't make sense. Anyway in the same manner as mutating the binding nil if this changed the literal data '(1 2 3) to '(43 1 2 3) would you then expect to get (43 1 2 3) every time you evaluated (1 2 3) from there on? I imagine that would be the only true effect of mutating a constant. If this was allowed you should be allowed to redefine 4 to be 5 so that evaluating 4 or (+ 2 2) shows the result 5.
I could manage following code to replaces items in a list using 2 other lists. Orilist and newlist have original and new terms in order. The replacement is done using orilist and newlist- if orilist items are present in slist, slist is changed to have corresponding new items from newlist:
(define (list-replace-from-lists slist orilist newlist)
(define replaced #f)
(define outl '())
(for ((item slist))
(set! replaced #f)
(for ((ori_ orilist) (i (in-naturals)) #:when (equal? item ori_))
(set! outl (cons (list-ref newlist i) outl))
(set! replaced #t))
(when (not replaced)
(set! outl (cons item outl))))
(reverse outl))
To replace 2 and 5 to 12 and 15, respectively, in (list 1 2 3 4 5 6) :
(list-replace-from-lists (list 1 2 3 4 5 6) (list 2 5) (list 12 15))
Output is:
'(1 12 3 4 15 6)
However, above code does not look functional and has many set! statements. How can this be converted to functional code? Should I use structures or some other data-types for above purpose?
Edit: items may recur in original list, e.g. (list 1 2 3 4 5 2 6)
You can still use lists and keep everything functional. :-) Here's my solution:
(define (replace-all haystack needles new-needles)
(define replace-alist (map cons needles new-needles))
(define (replace-one item)
(cond ((assoc item replace-alist) => cdr)
(else item)))
(map replace-one haystack))
Explanation of the code:
First, we build a replacement association list (alist). This is a list of pairs, of which the keys correspond to the needles and the values correspond to new-needles.
Then we define a replace-one function that takes an item, and sees if it matches any of the keys in the alist. If so, we return the corresponding value; otherwise, we return the original item.
Finally, we map the haystack through replace-one. Yay higher-order functions!
Note that this code is O(m*n) where m is the size of haystack and n is the size of needles, which is the same runtime as your version. If needles is large, you will want to use a hashtable instead of an alist, which will amortise the runtime of the function to O(m).
This is a functional solution that uses hash to keep the associations. That makes this solution O(haystack-length log needle-length) since immutable hashes are implemented with trees.
(define (list-replace-all haystack needles new-values)
;; make a dictionary of elements to be replaced
(define hash
(foldl (λ (needle new-value hash)
(hash-set hash needle new-value))
#hash()
needles
new-values))
;; do the replace. If not in hash the actual key is default
(map (λ (e) (hash-ref hash e e)) haystack))
(list-replace-all '(1 2 3 4 5 6) '(2 5) '(12 15))
; ==> (1 12 3 4 15 6)
Given a list, how do I process N items at a time? Ruby has each_slice method on the Enumerable that does this; what would be the Lisp equivalent?
Common Lisp's loop can be used for this very nicely, as in the following two examples. The first example loops for (x y z) in a list. However, the default step is cdr (rest), so if the list is (1 2 3 4 5), you get (1 2 3), (2 3 4), etc., for (x y z).
CL-USER> (loop for (x y z) on '(1 2 3 4 5 6 7 8 9 10 11 12)
do (print (list z y x)))
(3 2 1)
(4 3 2)
(5 4 3)
(6 5 4)
(7 6 5)
(8 7 6)
(9 8 7)
(10 9 8)
(11 10 9)
(12 11 10)
(NIL 12 11)
(NIL NIL 12)
NIL
If you do not want the overlap between iterations, specify the stepping function to be something that moves farther down the list. For instance, if you're pulling three elements at a time, use cdddr:
CL-USER> (loop for (x y z) on '(1 2 3 4 5 6 7 8 9 10 11 12) by 'cdddr
do (print (list z y x)))
(3 2 1)
(6 5 4)
(9 8 7)
(12 11 10)
NIL
Implementing partition with this technique
Another answer implemented the counterpart to each_slice using an auxiliary function. However, notice that partition (in that sense) is just each_slice with the identity function. This suggests that we should be able to implement it using the idiom above. The anonymous function
(lambda (list)
(nthcdr n list))
is the step function that we need. Since we do not know how many elements the cells have until run time, we can't bind each element like we did above with (x y z). We do have to match each tail of the list as we step down and extract the subsequence n elements. Here's a loop based implementation of partition.
CL-USER> (defun partition (list cell-size)
(loop for cell on list by #'(lambda (list)
(nthcdr cell-size list))
collecting (subseq cell 0 cell-size)))
PARTITION
CL-USER> (partition '(1 2 3 4 5 6) 2)
((1 2) (3 4) (5 6))
(defun partition-helper (lst acc x)
(if (< (length lst) x)
acc
(partition-helper (subseq lst x) (cons (subseq lst 0 x) acc) x)))
(defun partition (lst x)
(reverse (partition-helper lst '() x)))
Then you can:
[25]> (PARTITION '(1 2 3 4 5 6) 2)
((1 2) (3 4) (5 6))
or:
[26]> (PARTITION '(1 2 3 4 5 6) 3)
((1 2 3) (4 5 6))
and then just mapcar over the list to process it 2 or 3 elements at a time.
If you wanted to split your list on a predicate (as opposed to a fixed length sublists), I would have recommended nsplit-list.
For fixed length sublists, you may want to use loop:
(defun by-N (list n fun)
(loop for tail on list by (lambda (l) (nthcdr n l))
do (funcall fun (subseq tail 0 (min (length tail) n)))))
(by-n (loop for i from 0 to 20 collect i) 5 #'print)
(0 1 2 3 4)
(5 6 7 8 9)
(10 11 12 13 14)
(15 16 17 18 19)
(20)
Note that this is not very efficient (it scans the list more than necessary and allocates a fresh sublist for passing to fun).
The efficient version is more complicated:
(defun batch-map (list batch-size function)
"Call FUNCTION on sublists of LIST of size BATCH-SIZE.
Returns the list of return values of FUNCTION."
(do ((tail list (cdr end)) end ret (bs1 (1- batch-size)))
((endp tail) (nreverse ret))
(setq end (nthcdr bs1 tail))
(if (consp end)
(let ((next (cdr end)))
(setf (cdr end) nil)
(unwind-protect (push (funcall function tail) ret)
(setf (cdr end) next)))
(push (funcall function tail) ret))))
All the answers are practical and can be used, but I believe none replicates exactly the Ruby's behavior:
> 1.upto(7).each_slice(3) { |x, y, z| p [x, y, z] }
[1, 2, 3]
[4, 5, 6]
[7, nil, nil]
To emulate Ruby, I believe the proper code is something similar to:
CL-USER> (defun each-slice (n list thunk)
(apply thunk (loop for i below n collect (nth i list)))
(if (> (length list) n)
(each-slice n (subseq list n) thunk)))
Generates a similar response when called:
CL-USER> (each-slice 3 '(1 2 3 4 5 6 7) (lambda (x y z) (print (list x y z))))
(1 2 3)
(4 5 6)
(7 NIL NIL)
NIL
I need a function that concatenates multiple values into (simple) vector, similar to (concatenate ). However, unlike concatenate, it should be able to handle arguments that are not vectors or sequences.
I.e. it should work like this:
(concat #(1 2) 3) => #(1 2 3)
(concat 1 2 3) => #(1 2 3)
(concat 1 #(2 3 4)) => #(1 2 3 4)
(concat #(1 2) 2 #(3 4 5)) => #(1 2 3 4 5)
How can I do this? I think I've forgotten some trivial lisp construct that makes it possible.
As far as I can tell, concatenate can't do it. and I'm not quite sure how to use make it with macro (there's ,# consturct that inserts list into resulting lisp form, but but I'm not quite sure how to distinguish between non-sequences and sequences in this case).
The reduce approach in the other reply is quadratic in time.
Here is a linear solution:
(defun my-concatenate (type &rest args)
(apply #'concatenate type
(mapcar (lambda (a) (if (typep a 'sequence) a (list a)))
args)))
Since we can compute the length of the sequence, we can allocate the result sequence and then copy the elements into it.
(defun concat (type &rest items)
(let* ((len (loop for e in items
if (typep e 'sequence)
sum (length e)
else sum 1))
(seq (make-sequence type len)))
(loop with pos = 0
for e in items
if (typep e 'sequence)
do (progn
(setf (subseq seq pos) e)
(incf pos (length e)))
else
do (progn
(setf (elt seq pos) e)
(incf pos)))
seq))
CL-USER 17 > (concat 'string "abc" #\1 "def" #\2)
"abc1def2"
Above works well for vectors. A version for lists is left as an exercise.
defun my-concatenate (type &rest vectors)
(reduce (lambda (a b)
(concatenate
type
(if (typep a 'sequence) a (list a))
(if (typep b 'sequence) b (list b))))
vectors))
You can use reduce with a little modification of #'concatenate on your arguments. If one of the arguments is not a sequence, just transform it into a list (concatenate works even with mixed arguments of simple-vectors and lists).
CL-USER> (my-concatenate 'list #(1 2 3) 3 #(3 5))
(1 2 3 3 3 5)
CL-USER> (my-concatenate 'simple-vector #(1 2 3) 3 #(3 5))
#(1 2 3 3 3 5)
CL-USER> (my-concatenate 'simple-vector 1 #(2 3) (list 4 5))
#(1 2 3 4 5)
EDIT: well, you should probably accept the other answer.