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)))
Related
I'm pretty new to lisp and I want to make function that every even-indexed element replace it with new one element list that holds this element. For example
(1 2 3 4 5) -> (1 (2) 3 (4) 5), (1 2 3 4 5 6) -> (1 (2) 3 (4) 5 (6))
Right now I came up with solution that each of the lements put in it's own list, but I cant get exactly how to select every even-indexed element:
(DEFUN ON3 (lst)
((ATOM (CDR lst)) (CONS (CONS (CAR lst) NIL) NIL))
(CONS (CONS (CAR lst) NIL) (ON3 (CDR lst))))
Your code doesn't work. You'll need to use if or cond such that the code follow one of the paths in it. Right now you have an error truing to call a function called (atom (cdr lst)). If it had been something that worked it would be dead code because the next line is always run regardless. It is infinite recursion.
So how to count. You can treat every step as a handle on 2 elements at a time. You need to take care of the following:
(enc-odds '()) ; ==> ()
(enc-odds '(1)) ; ==> (1)
(enc-odds '(1 2 3 ...) ; ==> (1 (2) (enc-odds (3 ...))
Another way is to make a helper with extra arguments:
(defun index-elements (lst)
(labels ((helper (lst n)
(if (null lst)
lst
(cons (list (car lst) n)
(helper (cdr lst) (1+ n))))))
(helper lst 0)))
(index-elements '(a b c d))
; ==> ((a 0) (b 1) (c 2) (d 3))
For a non-recursive solution, loop allows for constructing simultaneous iterators:
(defun every-second (list)
(loop
for a in list
for i upfrom 1
if (evenp i) collect (list a)
else collect a))
(every-second '(a b c d e))
; ==> (A (B) C (D) E)
See http://www.gigamonkeys.com/book/loop-for-black-belts.html for a nice explanation of loop
I'm having trouble finding my error.
This keeps returning nil:
(even-greater-n 5 '(1 2 3 4 5 6 7))
(defun even-greater-n (n L)
(cond ((null L) nil)
((and (> (car L) n) (evenp n)) (car L))
(t (even-greater-n n (cdr L)))))
Your error
You are passing to evenp n
instead of (car L).
Iteration
This is relatively easy to implement using
loop:
(defun even-greater (n l)
(loop for k in l
when (and (< n k)
(evenp k))
return k))
(even-greater 5 '(1 2 3 4 5 6 7 8))
==> 6
Recursion
If you are required to use recursion, you can do it too:
(defun even-greater (n l)
(cond ((endp l) nil)
((and (< n (first l))
(evenp (first l)))
(first l))
(t (even-greater n (rest l)))))
(even-greater 3 '(1 2 3 4 5 6 7 8))
==> 4
Library
And, of course, Lisp has a very powerful library, including
find-if:
(defun even-greater (n l)
(find-if (lambda (k)
(and (< n k)
(evenp k)))
l))
(even-greater 2 '(1 2 3 4 5 6 7 8))
==> 4
You must look for (car L) is even or not.
Using find-if and a single, open-coded lambda function:
(defun even-greater (n list)
(find-if (lambda (item) (and (> item n) (evenp item))) list))
Using functional combinators:
;; Combine multiple functions with AND:
;; Returns a function of one-argument which
;; passes that argument to the functions in the list,
;; one by one. If any function returns nil, it stops
;; and returns nil. Otherwise it returns the value
;; returned by the last function:
(defun andf (&rest functions)
(lambda (arg)
(let (res)
(dolist (f functions res)
(unless (setf res (funcall f arg))
(return))))))
;; Returns a one-argument function which tests
;; whether its argument is greater than quant.
(defun greater (quant)
(lambda (arg) (> arg quant)))
;; "find it, if it is greater than n, and even"
(defun even-greater (n list)
(find-if (andf (greater n) #'evenp) list))
I have a problem with this function two-similar-p.
(defun two-similar-p (list1 list2)
(mapcar
(lambda (e)
(mapcar
(lambda (e1)
(if (equal e e1) t))
list2))
list1))
But is not correct use mapcar because this function returns a new list with T or NIL, but I need only to return a true or false.
ex.
(two-similar-p '(2 1 3) '(1 2 3))
==> ((NIL T NIL) (T NIL NIL) (NIL NIL T))
I was thinking to use recursion to compare the various elements, but I have no idea how to do that.
My function needs to work like:
(two-similar-p '(1 2 3) '(1 4 5)) ; ==> nil
(two-similar-p '(1 2 5) '(1 4 5)) ; ==> t
(two-similar-p '(1 2 6) '(6 4 2)) ; ==> t
Any advice?
The easiest "off-the-shelf" solution is to check that the intersection contains at least two elements:
(defun two-similar-p (l1 l2)
(consp (cdr (intersection l1 l2 :test #'equal))))
A slightly less OTS solution is to use hash tables:
(defun two-similar-p (l1 l2)
(let ((h1 (make-hash-table :test 'equal))
(common 0))
(dolist (x l1)
(setf (gethash x h1) t))
(dolist (x l2)
(when (gethash x h1)
(incf common))
(when (>= common 2)
(return t)))))
The advantage of the second approach is that its complexity is O(len(l1) + len(l2)),
while the mapcar approach will be O(len(l1) * len(l2)).
The standard does not specify the complexity of intersection and friends, but most implementations take good care of their users here (IOW, the complexity will be linear, not quadratic).
i am trying to write a function in lisp which have 2 parameters one function F and one list L
if i place '> in place of F and list L is '(1 2 3 4 5) it will return 5 as 5 is biggest.
and if we put '< then it compares all list elements and gives the smallest one as output.
and so on.
we can even put custom written function in place of F for comparison.
i wish i could provide more sample code but i am really stuck at the start.
(DEFUN givex (F L)
(cond
(F (car L) (car (cdr L))
;after this i got stuck
)
)
another attemp to write this function
(defun best(F list)
(if (null (rest list)) (first list)
(funcall F (first List) (best (F list)))))
You are almost there, just the else clause returns the f's return value instead of the the best element:
(defun best (F list)
(let ((first (first list))
(rest (rest list)))
(if (null rest)
first
(let ((best (best f rest)))
(if (funcall F first best)
best
first)))))
Examples:
(best #'< '(1 2 3))
==> 3
(best #'> '(1 2 3))
==> 1
Note that this recursive implementation is not tail-recursive, so it is not the most efficient one. You might prefer this instead:
(defun best (f list)
(reduce (lambda (a b) (if (funcall f a b) b a)) list))
Or, better yet,
(defmacro fmax (f)
`(lambda (a b) (if (,f a b) b a)))
(reduce (fmax <) '(1 2 3))
==> 1
(reduce (fmax >) '(1 -2 3 -4) :key #'abs)
==> 1
(reduce (fmax <) '(1 -2 3 -4) :key #'abs)
==> 4
How would I recurse through nested lists?
For example, given: '((A 1 2) (B 3 4))
How would I add 2 to the second element in each nested sublist?
(defun get-p0 (points)
(loop for x from 0 to
(- (list-length points) 1) do
(+ 2 (cadr (nth x points)))
)
)
I'm not really sure why (get-p0 '((A 1 2) (B 3 4))) returns NIL.
I'd go with something like this:
(loop for (letter x y) in '((A 1 2) (B 3 4))
collect (list letter (+ 2 x) y))
The reason: it's shorter and you don't measure the length of the list in order to iterate over it (why would you do that?)
Since you ask for a recursive solution:
(defun get-p0 (lst &optional (n 0))
(if (null lst)
nil
(let ((elt1 (first lst)) (eltn (cdr lst)))
(if (listp elt1)
(cons (get-p0 elt1) (get-p0 eltn))
(cons (if (= n 1) (+ elt1 2) elt1) (get-p0 eltn (+ n 1)))))))
so
? (get-p0 '((A 1 2) (B 3 4)))
((A 3 2) (B 5 4))
and it recurses further down if necessary:
? (get-p0 '((A 0 2) ((B -4 4) (C 10 4))))
((A 2 2) ((B -2 4) (C 12 4)))
The way you put it, you can consider the problem as a basic recursion pattern: you go through a list using recursion or iteration (mapcar, reduce, etc.; dolist, loop, etc.) and apply a function to its entries. Here is a functional solution:
(defun get-p0 (points)
(mapcar #'add-2 points))
where the auxiliary function can be defined as follows:
(defun add-2 (lst)
"Add 2 to the 2nd item"
(let ((res '()))
(do ((l lst (cdr l))
(i 1 (1+ i)))
((null l) (nreverse res))
(push (if (= 2 i)
(+ 2 (car l))
(car l))
res))))
As written your 'loop' use does not return anything; thus NIL is returned. As is your code is simply iterating over x and computing something; that something isn't stored anywhere.
So, how to get your desired result? Assuming you are willing to modify each point in points, this should work:
(defun get-p0 (points)
(loop for x from 0 to (- (list-length points) 1) do
(let ((point (nth x points)))
(setf (cadr point) (+ 2 (cadr point)))))
points)