I am struggling to find a function that will find a happy-medium between the get function and nth. I have been doing a lot of research on these sequence type functions, does anyone know a work around for this or know a function that performs as such?
I need nth's ability to grab sublists:
=> (nth '(1 (2 3) 4 5) 1)
(2 3)
=> (get 1 '(1 (2 3) 4 5))
nil
And I need get's ability to return "nil" when out of range:
=> (get -1 '(1 (2 3) 4 5))
nil
=> (nth '(1 (2 3) 4 5) -1)
Execution error (IndexOutOfBoundsException) at user/eval149 (REPL:1).
null
I need this code for a recursive sub-seq function:
(defn sub-seq [coll i j]
(nth coll i)
(if (< i (+ i j))
(sub-seq coll (inc' i) j)
)
)
(The sub-seq function is supposed to return 'j' elements starting at position 'i'.)
Here are some sample outputs for what I am trying to write:
=> (sub-seq '(1 2 (3 4) (5 (6 7))) 1 2))
(2 (3 4))
=> (sub-seq '(1 2 3 4 5 6 7) 2 4)
(3 4 5 6)
I finally got my function to work, thank you all for your help:
(defn sub-seq [coll i j]
(conj
(list*
(nth coll i nil)
(if (> j 1)
(sub-seq coll (+ i 1) (- j 1))))))
nth takes an optional third argument not-found. You can use it to provide a default value if your index is out of bounds:
user=> (nth '(1 (2 3) 4 5) -1)
Execution error (IndexOutOfBoundsException) at user/eval1 (REPL:1).
null
user=> (nth '(1 (2 3) 4 5) -1 nil)
nil
If you had a vector, you could use subvec directly:
(let [s [1 2 3 4 5 6 7]]
(subvec s 2 6))
If you have a sequence then you could write:
(defn subsequence [coll start n]
(->> coll
(drop start)
(take n)))
(subsequence '(1 2 (3 4) (5 (6 7))) 1 2)
=> (2 (3 4))
(subsequence '(1 2 3 4 5 6 7) 2 4)
=> (3 4 5 6)
Side note: When writing a Clojure program, often you can solve your problem more simply with sequence processing. Sometimes recursive algorithms are necessary, but often you can get by with Clojure's rich set of functions that operate on sequences.
You are misunderstanding get. It works on associative collections like maps and vectors. Consider:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(dotest
(let [data-list '(1 (2 3) 4 5)
data-vec (vec data-list) ]
(spyx (nth data-list 1))
(spyx (nth data-vec 1))
(spyx (get data-list 1))
(spyx (get data-vec 1))
))
with result
(nth data-list 1) => (2 3)
(nth data-vec 1) => (2 3)
(get data-list 1) => nil
(get data-vec 1) => (2 3)
Since a Clojure list is not associative like a map, list should not be used at all with lists. Ideally, get would throw an exception when passed a list argument to indicate it doesn't work with them.
For your specific goal, maybe just do something like:
(take j
(drop i coll))
For additional documentation please review here.
Related
I am trying to delete all occurrences of an element from a list, from any levels of the list. I am required to use a map function though. I am using Common Lisp. For example I'd want to be able to do:
(fdelete '(1 2 3 4 (3)) 3) => (1 2 4)
What I've tried so far:
This function will do what's needed, sort of. It will replace all occurences of the given element with NIL, so it's not exactly what I want.
(defun fdelete (l e)
(cond
((null l) 0)
((equal l e) nil)
((atom l) l)
(t (mapcar (lambda(l) (fdelete l e )) l ))
)
)
This will do
(fdelete '(1 2 3 4 (3)) 3) => (1 2 NIL 4 (NIL))
My second try is with the mapcap function, since this one won't return a list the same size as the input list.
This will do what's needed, but it will 'destroy' my initial list, as in, it will bring all sublists 'to surface'.
(defun fdelete (l e)
(cond
((null l) 0)
((equal l e) nil)
((atom l) (list l))
(t(mapcan(lambda(x) (fdelete x e ))l ))
)
)
So this indeed does (fdelete '(1 2 3 4 (3)) 3) => (1 2 4)
but it will also do it wrong if I for example try this:
(fdelete '(1 2 3 (4) (3)) 3)) => (1 2 4)
I'd want it to do (fdelete '(1 2 3 (4) (3)) 3)) => (1 2 (4))
I hope my question is well formed and detailed enough, and I am providing working examples. Can someone give me a few hints on how to solve this problem?
Using mapcan is the correct choice since you can wrap in list to get a value or use nil to get item removed. For the list element, if it doesn't already match what to delete, you should check the result of the recursion and wrap it if it's not the empty list.
The solution would look something like:
(defun remove-deep (item list)
(mapcan (lambda (cur)
(cond ((equal item cur) '())
...))
list))
(remove-deep 3 '(1 nil 2 3 (3) (3 4)))
; ==> (1 nil 2 (4))
To apply the principle of least surprise I have renamed the function since delete is the destructive version of remove. Also I kept the argument order of the standard functions:
I have a small exercise in Lisp:
Write a function test-delta with parameters delta and lst, which will
check if the difference between successive elements in lst is smaller than
delta. Write the function in two ways:
recursively
using a mapping function
I have no problem writing that function recursively, but I don't know which mapping function I should use. All the standard mapping functions work with only one element of the list at a time. reduce cannot be used either, because I do not have some operation to use between successive elements. What function could I use here?
All standard functions are working only with one element at time.
Reduce function cannot be use either
because i do not have some operation to use between to elements.
There's already an answer by uselpa showing that you can do this with reduce, but it feels a bit awkward to me to bend reduce to this case.
It's much more natural, in my opinion, to recognize that the standard mapping functions actually let you work with multiple lists. I'll show mapcar and loop first, and then every, which I think is the real winner here. Finally, just for completeness, I've also included maplist.
mapcar
The standard mapcar can take more than one list, which means that you can take elements from two different lists at once. Of particular note, it could take a list and (rest list). E.g.,
(let ((list '(1 2 3 4 5 6)))
(mapcar 'cons
list
(rest list)))
;=> ((1 . 2) (2 . 3) (3 . 4) (4 . 5) (5 . 6))
loop
You can use loop to do the same sort of thing:
(loop
with l = '(1 2 3 4 5 6)
for a in l
for b in (rest l)
collect (cons a b))
;=> ((1 . 2) (2 . 3) (3 . 4) (4 . 5) (5 . 6))
There are some other variations on loop that you can use, but some of them have less conventient results. E.g., you could loop for (a b) on list, but then you get a (perhaps) unexpected final binding of your variables:
(loop for (a b) on '(1 2 3 4 5 6)
collect (list a b))
;=> ((1 2) (2 3) (3 4) (4 5) (5 6) (6 NIL))
This is similar to what maplist will give you.
every
I think the real winners here, though, are going to the be every, some, notevery, and notany functions. These, like mapcar can take more than one list as an argument. This means that your problem can simply be:
(let ((delta 4)
(lst '(1 2 4 7 9)))
(every (lambda (x y)
(< (abs (- x y)) delta))
lst
(rest lst)))
;=> T
(let ((delta 2)
(lst '(1 2 4 7 9)))
(every (lambda (x y)
(< (abs (- x y)) delta))
lst
(rest lst)))
;=> NIL
maplist
You could also do this with maplist, which works on successive tails of the list, which means you'd have access to each element and the one following. This has the same 6 NIL at the end that the second loop solution did, though. E.g.:
(maplist (lambda (tail)
(list (first tail)
(second tail)))
'(1 2 3 4 5 6))
;=> ((1 2) (2 3) (3 4) (4 5) (5 6) (6 NIL))
reduce can be used:
(defun testdelta (delta lst)
(reduce
(lambda (r e)
(if (< (abs (- r e)) delta)
e
(return-from testdelta nil)))
lst)
t)
or, without return-from (but possibly slower):
(defun testdelta (delta lst)
(and
(reduce
(lambda (r e)
(and r (if (< (abs (- r e)) delta) e nil)))
lst)
t))
I am writing a clojure recursion function so that given:
(luty [1 2 3 4])
should have output like this:
((1 2 3 4) (2 3 4) (3 4) (4) ()
My code is:
(defn luty [a1]
(if (empty? a1)
(list )
(cons (seq a1) (luty (rest a1) )
)))
I am getting output:
((1 2 3 4) (2 3 4) (3 4) (4)) //comment missing a ()
Can anybody suggest me where I am getting wrong?
If we print out the process and look at the second to last opperation:
user> (defn luty [a1]
(println "a1 is" a1)
(if (empty? a1)
()
(cons (seq a1) (luty (rest a1)))))
#'user/luty
user> (luty [1 2 3 4])
a1 is [1 2 3 4]
a1 is (2 3 4)
a1 is (3 4)
a1 is (4)
a1 is ()
((1 2 3 4) (2 3 4) (3 4) (4))
user> (cons '(4) ())
((4))
We can see that the result of adding (4) to the empty list is ((4)) rather than ((4) ()) as you would most likely want. This can be fixed by making the base case a list containing the empty list, instead of just an empty list
user> (defn luty [a1]
(if (empty? a1)
'(())
(cons (seq a1) (luty (rest a1)))))
#'user/luty
user> (luty [1 2 3 4])
((1 2 3 4) (2 3 4) (3 4) (4) ())
The return value of cons is a list with as the first element the first argument and the rest of the list as the second argument. If the second argument is empty or nil, that means you get a list with the first argument as the single member.
The reason for this is that lists are (conceptually, at least, in clojure) linked lists with 2-space cells; one pointer for the head element and one pointer for the tail (another list, which in clojure is guaranteed to be a seq-like thing - in many other lisps you can set the second pointer to any value you want, so you're not guaranteed to get a "proper" list out of a cons there). A nil in the "tail" position marks the end of the list.
Lists are the most easily implemented and understandable persistent (in the clojure sense of immutable, structure-sharing) data structure.
Just to give you a different way of looking at it:
user> (defn luty [a1]
(reductions (fn [c _] (rest c)) (or (seq a1) '()) (-> a1 count range)))
=> #'user/luty
user> (luty [1 2 3 4])
=> ((1 2 3 4) (2 3 4) (3 4) (4) ())
user> (luty [])
=> (())
user> (luty nil)
=> (())
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.