i create static array, suggest would good for finding numbers in string for array size
(setf arr (make-array'(5)))
(loop for i from 0 to 4
do (if (/= nil (read-from-string str))
(setq x (read-from-string str t nil :start i))
(setf (aref arr i) x))))
i want to go until end of string
(defun string->vector (s)
(let ((*read-eval* nil)) ;be a bit safe
(with-input-from-string (in s)
(loop for e = (read in nil in)
for n upfrom 0
until (eq e in)
collect e into es
finally (return (make-array n :initial-contents es))))))
Related
(defun suma (L)
(setq var 0)
(do
((i 0 (+ i 1)))
((= i (length L)))
(+ var (nth i L)))
var)
Why does it always returns 0?
Shouldn't it return sum of list L?
+ does not modify its arguments, so, since you never modify var, its initial value of 0 is returned.
You need to replace (+ var (nth i L)) with (incf var (nth i L)), of, equivalently, (setq var (+ var (nth i L))).
See incf.
Note that you should bind var with let instead of making it global with setq.
Most importantly, note that your algorithm is quadratic in the length of the list argument (because nth scans your list every time from the start).
Here are some better implementations:
(defun sum-1 (l)
(reduce #'+ l))
(defun sum-2 (l)
(loop for x in l sum x))
(defun sum-3 (l)
(let ((sum 0))
(dolist (x l sum)
(incf sum x))))
Here is a bad implementation:
(defun sum-4 (l)
(apply #'+ l))
The problem with sum-4 is that it will fail if the length of the supplied list is larger than call-arguments-limit.
I thought this would be a comment for the full learning experience, but I was not able to put code in the comment.
There is a way to do sums without modifying any argument, and that is by doing it recursively:
(defun recsum (list)
(if list
(+ (first list) (recsum (rest list)))
0))
This version can be tail call optimized by the compiler, and as fast as a loop:
(defun recsum2 (list &optional (accumulator 0))
(if list
(recsum2 (rest list) (+ accumulator (first list)))
accumulator))
What you are trying to do could be done with do like this:
(defun suma (l)
(do
((var 0)
(i 0 (+ i 1)))
((= i (length l)) var)
(incf var (nth i l))))
But we don't usually do anything in dos body, so it's like this then:
(defun suma (l)
(do
((i 0 (+ i 1))
(var 0 (+ var (nth i l))))
((= i (length l)) var)))
But nth and length are slow, so better do it this way:
(defun suma (l)
(do*
((var (first l) (+ var (first list)))
(list (rest l) (rest list)))
((null list) var)))
This one is without the * in do, and returns 0 on empty list:
(defun suma (l)
(do
((acc 0 (+ acc (first list)))
(list l (rest list)))
((null list) acc)))
But my favorite is the reduce version from #sds which also can return 0 on empty list with :initial-value 0
EDIT: recsum2 did not return anything, so it needed a fix.
I have a line like "fun,arg1,arg2" <- it is a string
I split this string into list of strings by "," separator. Then I compare "fun" with some string (e.g. "Fibonacci").
Function for splitting (works fine)
(defun split-str (string &optional (r nil))
(let ((n (position "," string
:from-end t
:test #'(lambda (x y)
(find y x :test #'string=)))))
(if n
(split-str (subseq string 0 n)
(cons (subseq string (1+ n)) r))
(cons string r))))
Test function
(defun tmp (a)
(if (string= (nth 0 a) "Fibonacci")
(progn
(setf tab '())
(dolist (n (cdr a))
(cons tab '(parse-integer n))) ; parsing works fine (checked with write)
(write tab)) ; always NIL
;(apply #'parse-integer (apply #'values a)) - doesn't work
(write "nok")))
Calling:
(tmp (split-str "Fibonacci,15,33"))
Why my tab hasn't 2 elements?
cons doesn't change anything; it returns a new list using tab.
I'm trying to create a multidimensional array with initial content that is determined by a function. It is easy to do for any specified dimensionality of the array.
For example for 2x2 array:
(defmacro array-generation (fun &rest size-dimensions)
(let ((a (gensym))
(b (gensym)))
`(make-array ',size-dimensions
:initial-contents
(loop for ,a from 0 below (first ',size-dimensions) by 1
collect
(loop for ,b from 0 below (second ',size-dimensions) by 1
collect (,fun ,a ,b))))))
(defparameter bla (array-generation + 2 3))
Gives me #2A((0 1 2) (1 2 3)).
How do I generalize the macro for any dimensionality?
For example for 2x3x5x6x7x8
(defparameter bla (array-generation + 2 3 5 6 7 8))
(defmacro array-generation (fun &rest dims)
(let ((syms (loop :repeat (length dims) :collect (gensym))))
(reduce (lambda (x y) (append x (list y)))
(mapcar (lambda (sym dim)
`(loop for ,sym from 0 below ,dim by 1 collect))
syms dims)
:initial-value (cons fun syms)
:from-end t)))
You already have an answer, so I would just like to add this as food for thought. Three observations:
It's not necessary for array-generation to be a macro; it can be a function.
It may be easier to initialise the array explicitly instead of generating a list for consumption by :initial-contents.
The default initial value for from is 0, so it can be left out.
(defun array-generation (fn &rest dimensions)
(let ((array (make-array dimensions)))
(labels ((init (dims indices)
(if (null dims)
(setf (apply #'aref array indices) (apply fn indices))
(loop for i below (first dims) do
(init (rest dims) (cons i indices))))))
(init (reverse dimensions) nil)
array)))
I've a question, how to return a list without the nth element of a given list? E.g., given list: (1 2 3 2 4 6), and given n = 4, in this case the return list should be (1 2 3 4 6).
A simple recursive solution:
(defun remove-nth (n list)
(declare
(type (integer 0) n)
(type list list))
(if (or (zerop n) (null list))
(cdr list)
(cons (car list) (remove-nth (1- n) (cdr list)))))
This will share the common tail, except in the case where the list has n or more elements, in which case it returns a new list with the same elements as the provided one.
Using remove-if:
(defun foo (n list)
(remove-if (constantly t) list :start (1- n) :count 1))
butlast/nthcdr solution (corrected):
(defun foo (n list)
(append (butlast list (1+ (- (length list) n))) (nthcdr n list)))
Or, maybe more readable:
(defun foo (n list)
(append (subseq list 0 (1- n)) (nthcdr n list)))
Using loop:
(defun foo (n list)
(loop for elt in list
for i from 1
unless (= i n) collect elt))
Here's an interesting approach. It replaces the nth element of a list with a new symbol and then removes that symbol from the list. I haven't considered how (in)efficient it is though!
(defun remove-nth (n list)
(remove (setf (nth n list) (gensym)) list))
(loop :for i :in '(1 2 3 2 4 6) ; the list
:for idx :from 0
:unless (= 3 idx) :collect i) ; except idx=3
;; => (1 2 3 4 6)
loop macro can be very useful and effective in terms of generated code by lisp compiler and macro expander.
Test run and apply macroexpand above code snippet.
A slightly more general function:
(defun remove-by-position (pred lst)
(labels ((walk-list (pred lst idx)
(if (null lst)
lst
(if (funcall pred idx)
(walk-list pred (cdr lst) (1+ idx))
(cons (car lst) (walk-list pred (cdr lst) (1+ idx)))))))
(walk-list pred lst 1)))
Which we use to implement desired remove-nth:
(defun remove-nth (n list)
(remove-by-position (lambda (i) (= i n)) list))
And the invocation:
(remove-nth 4 '(1 2 3 2 4 6))
Edit: Applied remarks from Samuel's comment.
A destructive version, the original list will be modified (except when n < 1),
(defun remove-nth (n lst)
(if (< n 1) (cdr lst)
(let* ((p (nthcdr (1- n) lst))
(right (cddr p)))
(when (consp p)
(setcdr p nil))
(nconc lst right))))
That's elisp but I think those are standard lispy functions.
For all you haskellers out there, there is no need to twist your brains :)
(defun take (n l)
(subseq l 0 (min n (length l))))
(defun drop (n l)
(subseq l n))
(defun remove-nth (n l)
(append (take (- n 1) l)
(drop n l)))
My horrible elisp solution:
(defun without-nth (list n)
(defun accum-if (list accum n)
(if (not list)
accum
(accum-if (cdr list) (if (eq n 0) accum (cons (car list) accum))
(- n 1))))
(reverse (accum-if list '() n)))
(without-nth '(1 2 3) 1)
Should be easily portable to Common Lisp.
A much simpler solution will be as follows.
(defun remove-nth (n lst)
(append (subseq lst 0 (- n 1)) (subseq lst n (length lst)))
)
I want to change the nth element of a list and return a new list.
I've thought of three rather inelegant solutions:
(defun set-nth1 (list n value)
(let ((list2 (copy-seq list)))
(setf (elt list2 n) value)
list2))
(defun set-nth2 (list n value)
(concatenate 'list (subseq list 0 n) (list value) (subseq list (1+ n))))
(defun set-nth3 (list n value)
(substitute value nil list
:test #'(lambda (a b) (declare (ignore a b)) t)
:start n
:count 1))
What is the best way of doing this?
How about
(defun set-nth4 (list n val)
(loop for i from 0 for j in list collect (if (= i n) val j)))
Perhaps we should note the similarity to substitute and follow its convention:
(defun substitute-nth (val n list)
(loop for i from 0 for j in list collect (if (= i n) val j)))
BTW, regarding set-nth3, there is a function, constantly, exactly for situation like this:
(defun set-nth3 (list n value)
(substitute value nil list :test (constantly t) :start n :count 1))
Edit:
Another possibility:
(defun set-nth5 (list n value)
(fill (copy-seq list) value :start n :end (1+ n)))
It depends on what you mean for "elegance", but what about...
(defun set-nth (list n val)
(if (> n 0)
(cons (car list)
(set-nth (cdr list) (1- n) val))
(cons val (cdr list))))
If you have problems with easily understanding recursive definitions then a slight variation of nth-2 (as suggested by Terje Norderhaug) should be more "self-evident" for you:
(defun set-nth-2bis (list n val)
(nconc (subseq list 0 n)
(cons val (nthcdr (1+ n) list))))
The only efficiency drawback I can see of this version is that traversal up to nth element is done three times instead of one in the recursive version (that's however not tail-recursive).
How about this:
(defun set-nth (list n value)
(loop
for cell on list
for i from 0
when (< i n) collect (car cell)
else collect value
and nconc (rest cell)
and do (loop-finish)
))
On the minus side, it looks more like Algol than Lisp. But on the plus side:
it traverses the leading portion of the input list only once
it does not traverse the trailing portion of the input list at all
the output list is constructed without having to traverse it again
the result shares the same trailing cons cells as the original list (if this is not desired, change the nconc to append)