averaging
averaging
hi,
trying to solve this problem:
a function that adds up the numbers at the end of lists and then averages them.
input is like (xxx 1) (yyy 2) (zzz 3)....(nnn n)
=>6 ;(1+2+3+..+n)/n
(defun averaging (lst)
(if (null lst)
0
(+(setq(last lst)?
how does it register the number of lists it has visited, which will be the denominator of the average?
there is some sort of recursion i guess..
thnx
trying to solve this problem:
a function that adds up the numbers at the end of lists and then averages them.
input is like (xxx 1) (yyy 2) (zzz 3)....(nnn n)
=>6 ;(1+2+3+..+n)/n
(defun averaging (lst)
(if (null lst)
0
(+(setq(last lst)?
how does it register the number of lists it has visited, which will be the denominator of the average?
there is some sort of recursion i guess..
thnx
Re: averaging
If the input is a list of lists like:
and it's always the same math operation [e.g. addition] and used always with the same element in the lists [e.g. the last element] then I would try to use REDUCE [look at the examples at the bottom of the page] instead of recursion to compute the sum of the numbers.
Hint: with the input list from above, the denominator is equal to the LENGTH of the list.
Code: Select all
(list ('xxx 1) ('yyy 2) ('zzz 3) ... ('nnn n))
Hint: with the input list from above, the denominator is equal to the LENGTH of the list.
Re: averaging
I'm sure this is bad but:
Code: Select all
(defun average-list (lst)
(/
(eval (append '(+)
(mapcar #'(lambda (x) (first (last x))) lst)))
(length lst)))
-
- Posts: 61
- Joined: Mon Jul 07, 2008 8:06 pm
- Location: Toowoomba, Queensland, Australia
- Contact:
Re: averaging
Good idea, try apply rather than eval with append:s-imp wrote:I'm sure this is bad ...
Code: Select all
(defun average-list (lst)
(/ (apply #'+
(mapcar #'(lambda (x) (first (last x))) lst))
(length lst)))
Hint: You'd want to use the same function you passed to mapcar as the :key argument to reduce.
Re: averaging
Here's a closure, just for fun:
(mapcar #'cadr numbers) can be passed as the argument to APPLY to fit the pair structure.
Just as an experiment, I tried writing a recursive solution that would keep the denominator in an accumulator instead of using LENGTH. My tinkering ended at this...
Of course, the accumulator returns 0 on the last frame, so the function ends with a divide-by-zero no matter what. Other than putting the accumulator outside the scope of LABELS (which is probably why I used LABELS in the first place) I'm not sure how to deal with this, or if it would be in any way better than just using LENGTH.
Code: Select all
(defun make-averager ()
(let ((numbers nil))
(lambda (lst)
(setf numbers (nconc numbers lst))
(/ (apply #'+ numbers) (length numbers)))))
CL-USER> (defvar pants (make-averager))
PANTS
CL-USER> (funcall pants (list 1 2))
3/2
CL-USER> (funcall pants (list 1 2))
3/2
CL-USER> (funcall pants (list 3 4))
13/6
Just as an experiment, I tried writing a recursive solution that would keep the denominator in an accumulator instead of using LENGTH. My tinkering ended at this...
Code: Select all
(defun avg (lst)
(labels ((rec (numbers acc)
(if (null numbers)
0
(values (+ (car numbers) (rec (cdr numbers)
(1+ acc))) acc))))
(multiple-value-bind (sum den) (rec lst 0)
(/ sum den))))
"If you want to improve, be content to be thought foolish and stupid." -Epictetus
Re: averaging
Thanks Kohath.
reduce worked beautifully. And I knew something was wrong when I used 'eval'.
reduce worked beautifully. And I knew something was wrong when I used 'eval'.
Re: averaging
I like the idea of closures, though it is a bit above my pay-grade at the moment, Duke.
I had another solution that used 'let' and 'dolist', but I didn't want to use setf. It must have been something I read somewhere about minimizing their usage. Is that just for a style of programming using Common Lisp or is there a genuine price for using 'setf'.
(I also should have used cons in my first attempt rather than append, I'm hazarding to guess.)
I had another solution that used 'let' and 'dolist', but I didn't want to use setf. It must have been something I read somewhere about minimizing their usage. Is that just for a style of programming using Common Lisp or is there a genuine price for using 'setf'.
(I also should have used cons in my first attempt rather than append, I'm hazarding to guess.)
Re: averaging
I happened to re-read chapter 3 of Paul Graham's On Lisp this afternoon, and in that very chapter the author opines that programmers should consider non-functional functions (i.e., functions that produce or use side-effects) like SETF to have a "tax" attached to them. You might say the tax is on maintainability and extensibility, which you'll pay in spades when it comes to debugging a larger project, I think.s-imp wrote:I like the idea of closures, though it is a bit above my pay-grade at the moment, Duke.
I had another solution that used 'let' and 'dolist', but I didn't want to use setf. It must have been something I read somewhere about minimizing their usage. Is that just for a style of programming using Common Lisp or is there a genuine price for using 'setf'.
(I also should have used cons in my first attempt rather than append, I'm hazarding to guess.)
Incidentally, Graham discusses closures in chapter 5.
"If you want to improve, be content to be thought foolish and stupid." -Epictetus
Re: averaging
Now here is a working 'reduce' example:
The :key argument tells 'reduce' to which element from the lists inside 'lst' the '+' function shall be applied. Because 'last' returns the last element as a list, the element itself must be extracted with 'first'.
Also note that '/' often returns ratios, which are more precise than floats, but sometimes the result is not-so obvious:
Here '7/2' is the simplified version of '21/6'.
Embarrassing but true, I had to verify this with a commandline calculator...
Warning: using too much Common Lisp can make you lazy in your brain.
Code: Select all
(defun average (lst)
(/ (reduce #'+ lst :key #'(lambda (x) (first (last x))))
(length lst)))
Also note that '/' often returns ratios, which are more precise than floats, but sometimes the result is not-so obvious:
Code: Select all
(average (list '(a 1) '(b 2) '(c 3) '(d 4) '(e 5) '(f 6)))
=> 7/2
Embarrassing but true, I had to verify this with a commandline calculator...
Warning: using too much Common Lisp can make you lazy in your brain.
Re: averaging
