I'm trying to write a function that takes two parameters, a list and the number of times to loop, n. I've been trying to implement a let loop to call the helper function, save the result to a variable, then decrement n by 1 each loop and then stop when n reaches 0, but I'm not sure how exactly to do it..
Here's my code:
; helper function to queue it once
(define (queue lst)
(if (empty? lst)
'()
(append (cdr lst) (list (car lst)))))
;main function that calls helper function
(define (queueLoop n lst)
(if (empty? lst)
'()
(let loop ((res (queue lst))
(lst (queue lst)))
(cond
[(> n 0)
((- n 1) (loop (queue res) (rest lst)))]
(else
(loop (queue res) (rest lst)))))))
Here is what I would have done:
(define (rotate-left-inc lst n)
(let-values ([(tail head) (split-at lst n)])
(append head tail)))
As for a roll your own, using the fact that cons is O(1) and append is O(n) I'd do:
;; rotate once in O(n) time
(define (rotate-left-once lst)
(append (cdr lst) (list (car lst))))
;; rotate n times in O(n) time
(define (rotate-left-on lst n)
(let loop ([head lst] [rtail '()] [n n])
(if (<= n 0)
(append head (reverse rtail))
(loop (cdr head) (cons (car head) rtail) (sub1 n)))))
While there is no way to repeat rotate-once and get a efficient procedure:
;; rotate n times in O(n^2) time
(define (rotate-left lst n)
(let loop ([n n] [lst lst])
(if (<= n 0)
lst
(loop (sub1 n) (rotate-left-once lst)))))
This get quite slow the longer the number you rotate when using the naive version, while the one using append once is much faster:
(define lst1 (make-list 200 198))
(define lst2 (make-list 20000 19998))
(define lst3 (make-list 2000000 1999998))
(for-each (lambda (lst)
(display (car lst))
(newline)
(display "O(n) inc")
(time (rotate-left-inc lst (car lst)))
(display "O(n) roll")
(time (rotate-left-on lst (car lst)))
(display "O(n^2)")
(time (rotate-left lst (car lst))))
(list lst1 lst2 lst3))
Output on my computer clearly shows the exponential time takes a lot of time:
198
O(n) inccpu time: 1 real time: 0 gc time: 0
O(n) rollcpu time: 0 real time: 0 gc time: 0
O(n^2)cpu time: 0 real time: 1 gc time: 0
19998
O(n) inccpu time: 1 real time: 0 gc time: 0
O(n) rollcpu time: 0 real time: 1 gc time: 0
O(n^2)cpu time: 4846 real time: 4884 gc time: 1295
1999998
O(n) inccpu time: 207 real time: 209 gc time: 160
O(n) rollcpu time: 279 real time: 282 gc time: 234
O(n^2) (didn't wait for it. Gave up after 5 minutes)
Remember that in Scheme most operations don't mutate variables, instead they return a new value with the modification. Take this, for instance:
(- n 1)
The above line is not modifying the value of n, it's returning a new value that is equal to n minus 1, and unless you store it somewhere or pass it as a parameter to a function call, the value will be lost (that's what's happening, in fact).
UPDATE: Now that you've posted a sample input/output, it's clear what you intended to do. Here's another simple way to write a solution using built-in procedures, which handles corner cases:
(define (queueLoop n lst)
(let ((x (min n (length lst))))
(append (drop lst x)
(take lst x))))
For example:
(queueLoop 1 '())
=> '()
(queueLoop 0 '(1 2 3 4))
=> '(1 2 3 4)
(queueLoop 3 '(1 2 3 4))
=> '(4 1 2 3)
(queueLoop 5 '(1 2 3 4))
=> '(1 2 3 4)
Related
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 start studying Lisp and I find a code on the book as example but I do not understand what is it for. Are you able to help me understanding that? I don't know if it is the right place to do it. Thanks everyone
(defun compress (l1)
(cond ((null (cdr l1)) '())
(t (accumula (car l1) 1 (cdr l1)))))
(defun accumula (val acc lst)
(cond ((null lst) (cons (comp-list val acc) nil))
((eq val (car lst)) (accumula val (1+ acc) (cdr lst)))
(t (cons (comp-list val acc) (accumula (car lst) 1 (cdr lst))))))
(defun comp-list (val acc)
(if (> acc 1) (list acc val) val))
It's a compression function, of the Run Length Encoding variety.
(compress '(3 3 4 3 3 2 1 1 1 1 0))
will yield
((2 3) 4 (2 3) 2 (4 1) 0)
where the first number in each sublist is the number of times the second number repeats in the original sequence.
It doesn't look like much from the example, but for long sequences where numbers repeat a lot, you can get significant savings in storage costs.
This is an answer to problem 13 in The 99 Lisp problems (L99). It has a bug:
(compress '(a))
; ==> nil
The correct result would have been (a).
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)
I am just trying to learn some Lisp, so I am going through project euler problems. I found problem no. 14 interesting (so if you are planning to solve this problems stop reading now, because I pasted my solution at the bottom). With my algorithm it was so slow, but after using memoization (I copied the function from Paul Graham's "on Lisp" book) it was much more faster (around 4 to 8 seconds).
My question is about this bunch of warnings that I got:
Am I doing something wrong? Can I improve my style?
> ;; Loading file
> /euler-lisp/euler-14.lisp
> ... WARNING in COLLATZ-SERIE :
> COLLATZ-SERIE-M is neither declared
> nor bound, it will be treated as if it
> were declared SPECIAL. WARNING in
> COLLATZ-SERIE : COLLATZ-SERIE-M is
> neither declared nor bound, it will be
> treated as if it were declared
> SPECIAL. WARNING in COMPILED-FORM-314
> : COLLATZ-SERIE-M is neither declared
> nor bound, it will be treated as if it
> were declared SPECIAL. (525 837799)
> Real time: 18.821894 sec. Run time:
> 18.029127 sec. Space: 219883968 Bytes GC: 35, GC time: 4.080254 sec. Las
> siguientes variables especiales no han
> sido definidas: COLLATZ-SERIE-M 0
> errores, 0 advertencias ;; Loaded file
This is the code:
(defun collatz (n)
(if (evenp n) (/ n 2) (+ (* 3 n) 1)))
(defun memoize (fn)
(let ((cache (make-hash-table :test #'equal)))
#'(lambda (&rest args)
(multiple-value-bind (val win) (gethash args cache)
(if win
val
(setf (gethash args cache)
(apply fn args)))))))
(defun collatz-serie (n)
(cond ((= n 1) (list 1))
((evenp n) (cons n (funcall collatz-serie-m (/ n 2))))
(t (cons n (funcall collatz-serie-m (+ (* 3 n) 1))))))
(defun collatz-serie-len (n)
(length (collatz-serie n)))
(setq collatz-serie-m (memoize #'collatz-serie))
(defun gen-series-pairs (n)
(loop for i from 1 to n collect
(list (collatz-serie-len i) i)))
(defun euler-14 (&key (n 1000000))
(car (sort (gen-series-pairs n) #'(lambda (x y) (> (car x) (car y))))))
(time (print (euler-14)))
Thanks a lot, and forgive the probable errors, I am just beginning with Lisp.
Br
UPDATE:
i want to share the final code that i wrote. using custom external hash table for memoization and improving the final loop.
(defvar *cache* (make-hash-table :test #'equal))
(defun collatz (n)
(if (evenp n) (/ n 2) (+ (* 3 n) 1)))
(defun collatz-serie (n)
(cond ((= n 1) (list 1))
((evenp n) (cons n (collatz-serie (/ n 2))))
(t (cons n (collatz-serie (+ (* 3 n) 1))))))
(defun collatz-serie-new (n)
(labels ((helper (n len)
(multiple-value-bind (val stored?) (gethash n *cache*)
(if stored?
val
(setf (gethash n *cache*) (cond ((= n 1) len)
((evenp n) (+ len (helper (/ n 2) len)))
(t (+ len (helper (+ (* 3 n) 1) len)))))))))
(helper n 1)))
;; learning how to loop
(defun euler-14 (&key (n 1000000))
(loop with max = 0 and pos = 0
for i from n downto 1
when (> (collatz-serie-new i) max)
do (setf max (collatz-serie-new i)) and do (setf pos i)
finally (return (list max pos))))
It is bad style to setq an unknown name. It is assumed that you mean to create a new global special variable, then set it, but this should be made explicit by introducing these bindings first. You do this at the top level by using defvar (or defparameter or defconstant) instead, and in lexical blocks by using let, do, multiple-value-bind or similar constructs.
I need to write a non-recursive version of the function sum-squares and Use a do-loop that is based on the length of the argument list.
Here's how it's done generally:
(defun sum-squares (list) (loop for x in list
for y = (* x x)
summing y into total
finally (return total)))
A do loop solution is even simpler, but not half as elegant:
(defun sum-squares (list)
(let ((sum 0)) (do ((i 0 (1+ i)))
((>= i (length list)))
(setq sum (+ sum (* (nth i list) (nth i list)))))
sum))