I need to produce a list containing the averages of a lists of lists where the number of sub-lists could vary. So given the input list:
((l1a l1b l1c) (l2a l2b l2c) (l3a l3b l3c)...)
the output would be:
(average(l1a l2a l3a) average(l1b l2b l3b) average(l1c l2c l3c)...).
I'm sure there's a really elegant way to do this in lisp but I don't know where to start. Any advice would be gratefully received.
CL-USER 27 > (let* ((list-of-lists '((1.0 2.0 3.0)
(1.0 3.0 5.0)
(1.0 4.0 6.0)
(1.0 5.0 8.0)))
(length (length list-of-lists)))
(mapcar (lambda (x)
(/ x length))
(reduce (lambda (l1 l2)
(mapcar #'+ l1 l2))
list-of-lists)))
(1.0 3.5 5.5)
Related
I am working on a problem that calls for me to compute the grade point average for each student in a class.
The input is a lisp file with the following format:
( ((name studentname) (class hour grade) (class hour grade) ...)
((name studentname) (class hour grade) (class hour grade) ...) ...)
For the output: I need to print the students name and their GPA (average of the grades for that student) sorted by average grade as well as the class average (the average of the grades for each unique class).
So far this is what I have
(setq class '(((name Seymore) (eng 3 4.0) (mat 3 3.0) (his 3 4.0) (bio 3 2.0) (biol 1 4.0))
((name Ichahbod) (cs 3 3.0) (mat 3 4.0) (spe 2 4.0) (che 3 4.0) (chel 1 3.0) (lit 3 3.0))
((name Zackery) (mat 5 3.0) (eng 3 3.0) (jou 2 3.0) (phy 3 3.0) (phyl 1 4.0) (lit 2 4.0))
((name Tukerville) (soc 4 3.0) (mus 2 4.0) (jou 3 4.0) (geo 4 4.0) (geol 1 3.0) (eng 3 3.0))
((name Simonsays) (css 3 3.0) (ast 3 4.0) (spe 3 4.0) (cs 3 4.0) (spe 2 3.0) (dan 4 4.0))
((name Snicker) (eng 3 4.0) (phy 4 4.0) (css 3 2.0) (csl 1 4.0) (ped 2 3.0) (mat 3 3.0))
((name Glass) (mat 3 1.0) (eng 3 1.0) (ped 1 1.0) (bio 3 1.0) (biol 1 0.0) (che 3 1.0) (chel 1 1.0))))
;this function multiplies the hours * the grades
(defun product (hours grades)
(* hours grades)
)
;this function multiplies a set of grades
(defun sumofGrades (L)
(cond
((null L) 0) ;check if it is first
(t (+ (product (cdr (cdadar L)) (caddar L)))) ;first val then the second val
(sumofGrades (cdr L)) ;the rest of one
)
)
;to get the total , same as sum of grades but sum the second variables
(defun totalHours (L)
(cond
((null L) 0) ;check if it is first
(t (+ (product (caddar L) (caddar L)))) ;first val then the second val
(totalHours() (cdr L)) ;the rest of one
)
)
(defun gradepoint (L)
( / (sumofGrades L) (totalHours L))
)
I attempted to start with the auxiliary methods because I thought that would be the best approach, it might not have been. When I run sumofGrades, I get back the 4.0 like I need from the first entry but it says it is not a number. I wrote these methods going off of the basic math that i need to do with the numbers but at this point I am confused on what to do next.
If I need to rewind and go a different routine I am down, any help would be appreciated.
First define some generic average function:
(defun average (lst &key (key #'identity))
(when lst
(/ (reduce #'+ lst :key key) (length lst))))
Define also a grade function to retrieve the grade of a given student in a given class (not necessary but will make it more clear):
(defun grade (class) (caddr class))
and a grades function to retrieve the grades of a student:
(defun grades (student)
(cdr (find student class :key #'cadar)))
Now you can find the average of the grades of a student by calling
(average (grades 'seymore ) :key #'grade)
=> 3.4
Following this example, you should be able to write the average of all the class by yourself.
Your code
(defun sumofGrades (L)
(cond
((null L) 0) ;check if it is first
(t (+ (product (cdr (cdadar L)) (caddar L)))) ;first val then the second val
(sumofGrades (cdr L)) ;the rest of one
)
)
Let's look at it:
(defun sumofGrades (L) ; please no camelCase in Lisp
(cond
((null L) 0) ;check if it is first <- what does this comment mean???
(t (+ (product (cdr (cdadar L)) (caddar L))))
; what is (+ (product (cdr (cdadar L)) (caddar L))) ?
; you are calling + with one argument. Why?
; what does a function like caddar mean?
; what is it supposed to do?
; no one reading your code will have an idea why
; caddar and not cdaadar, cdadaadr, or cdddddr...
; write better documented, or self-documenting code.
(sumofGrades (cdr L)) ;the rest of one <- what does this comment mean?
; what is (sumofGrades (cdr L)) ?
; is sumofGrades a variable checked in COND?
; should it be a function call?
; just as it is alone here, it does not make any sense.
; since T is always true, this clause is also never reached...
) ; <- please no dangling parentheses in Lisp
)
When compiling the above function, LispWorks says:
; (TOP-LEVEL-FORM 0)
;;;*** Warning in SUMOFGRADES: The following cond clause
;;; will never be processed: ((SUMOFGRADES (CDR L)))
Summary: sumofGrades won't work. A Lisp compiler already complains about it.
More about style
Global variables: they are defined by DEFPARAMETER or DEFVAR. Don't use SETQ.
Don't write
(setq class ...)
instead write:
(defparameter *class* ...
"the global variable *class* is a list of ...")
You probably want to try reduce:
(mapcar (lambda (l)
(cons (second (first l))
(/ (reduce #'+ (rest l) :key #'third)
(1- (length l)))))
class)
==>
((SEYMORE . 3.4) (ICHAHBOD . 3.5) (ZACKERY . 3.3333333) (TUKERVILLE . 3.5)
(SIMONSAYS . 3.6666667) (SNICKER . 3.3333333) (GLASS . 0.85714287))
then you can sort this using sort:
(sort * #'< :key #'cdr)
==>
((GLASS . 0.85714287) (ZACKERY . 3.3333333) (SNICKER . 3.3333333) (SEYMORE . 3.4)
(ICHAHBOD . 3.5) (TUKERVILLE . 3.5) (SIMONSAYS . 3.6666667))
here * is the value of the previous expression.
PS. Since this is probably h/w, I am giving a code sample rather than a complete solution, I suggest that you play with my code and then ask another very specific question if something is not unclear.
PPS. A few stylistic remarks:
do not define functions like your product, it's just confusing noise
do not use the CamelCase, use normal-lisp-dashes instead
do not use hanging parens
use Emacs to indent your code, it is unreadable now.
I am very new to LISP (so forgive me for any dumb mistakes) and the first lab of the year states:
Define a function, STDEV that will compute the standard deviation of a list of numbers (look up formula)
I wrote this code but I don't know why it refuses to work:
(defun stdev (x)
(sqrt (/ (apply '+ (expt (- x (/ (apply '+ x)
(length x)))
2))
(length x))))
(setq a '(1 2 3 4 5))
(STDEV a)
But on runtime it produces the error:
(1 2 3 4 5) is not a number
I believe that I have correctly emulated the standard deviation formula (though I wouldn't put it past myself to make a dumb mistake), but why does my program not like the list of numbers that I give it to evaluate? It is most likely a simple mistake with inputs from this new style of coding but any and all help is greatly appreciated!
Use indentation. I've edited your question:
(defun stdev (x)
(sqrt (/ (apply '+ (expt (- x (/ (apply '+ x)
(length x)))
2))
(length x))))
expt returns a number. You call (apply '+ some-number)?
Also you subtract a number from a list.
Why?
Generally I would recommend to use a Lisp listener (aka REPL) to get to working code:
Compute the mean value:
CL-USER 21 > (let ((l (list 1 2 3 4 5)))
(/ (reduce #'+ l)
(length l)))
3
Subtract the mean value and square using mapcar:
CL-USER 22 > (mapcar (lambda (item)
(expt (- item 3) 2))
(list 1 2 3 4 5))
(4 1 0 1 4)
Compute the variance as the mean value of above:
CL-USER 23 > (let ((l (list 4 1 0 1 4)))
(/ (reduce #'+ l)
(length l)))
2
Take the square root to get the standard deviation:
CL-USER 24 > (sqrt 2)
1.4142135
Then you only need to assemble it into a few functions: average, variance and standard-deviation.
You’re taking - a ..., when a is your list.
Not a complete answer, because this is homework, but: you want to calculate the mean first, you can implement a sum function, which you will need twice, with a fold, and you can apply a helper function or lambda expression to every element of a list using a map.
I want to define a function that would have the following properties:
(almost-equal? (cos (/ pi 2)) 0.0) ; --> #t
For doing that I thought that I should use flulp in the following way:
(define (almost-equal? a b)
(let [[epsilon (max (abs (flulp (* 10.0 a))) (abs (flulp (* 10.0 b))))]]
(<= (absolute-error a b) epsilon)))
But it failed to pass my test. Do we have a canonical way of doing such comparison? Any advise are welcome.
Use relative-error: documentation
I can recommend this article on how to debug numerical functions:
Practically Accurate Floating-Point Math
by Neil Toronto and Jay McCarthy
I want to calculate the sum of digits of a number in Scheme. It should work like this:
>(sum-of-digits 123)
6
My idea is to transform the number 123 to string "123" and then transform it to a list '(1 2 3) and then use (apply + '(1 2 3)) to get 6.
but it's unfortunately not working like I imagined.
>(string->list(number->string 123))
'(#\1 #\2 #\3)
Apparently '(#\1 #\2 #\3) is not same as '(1 2 3)... because I'm using language racket under DrRacket, so I can not use the function like char->digit.
Can anyone help me fix this?
An alternative method would be to loop over the digits by using modulo. I'm not as used to scheme syntax, but thanks to #bearzk translating my Lisp here's a function that works for non-negative integers (and with a little work could encompass decimals and negative values):
(define (sum-of-digits x)
(if (= x 0) 0
(+ (modulo x 10)
(sum-of-digits (/ (- x (modulo x 10)) 10)))))
Something like this can do your digits thing arithmetically rather than string style:
(define (digits n)
(if (zero? n)
'()
(cons (remainder n 10) (digits2 (quotient n 10))))
Anyway, idk if its what you're doing but this question makes me think Project Euler. And if so, you're going to appreciate both of these functions in future problems.
Above is the hard part, this is the rest:
(foldr + (digits 12345) 0)
OR
(apply + (digits 1234))
EDIT - I got rid of intLength above, but in case you still want it.
(define (intLength x)
(define (intLengthP x c)
(if (zero? x)
c
(intLengthP (quotient x 10) (+ c 1))
)
)
(intLengthP x 0))
Those #\1, #\2 things are characters. I hate to RTFM you, but the Racket docs are really good here. If you highlight string->list in DrRacket and hit F1, you should get a browser window with a bunch of useful information.
So as not to keep you in the dark; I think I'd probably use the "string" function as the missing step in your solution:
(map string (list #\a #\b))
... produces
(list "a" "b")
A better idea would be to actually find the digits and sum them. 34%10 gives 4 and 3%10 gives 3. Sum is 3+4.
Here's an algorithm in F# (I'm sorry, I don't know Scheme):
let rec sumOfDigits n =
if n<10 then n
else (n%10) + sumOfDigits (n/10)
This works, it builds on your initial string->list solution, just does a conversion on the list of characters
(apply + (map (lambda (d) (- (char->integer d) (char->integer #\0)))
(string->list (number->string 123))))
The conversion function could factored out to make it a little more clear:
(define (digit->integer d)
(- (char->integer d) (char->integer #\0)))
(apply + (map digit->integer (string->list (number->string 123))))
(define (sum-of-digits num)
(if (< num 10)
num
(+ (remainder num 10) (sum-of-digits (/ (- num (remainder num 10)) 10)))))
recursive process.. terminates at n < 10 where sum-of-digits returns the input num itself.
I'm just playing around with scheme/lisp and was thinking about how I would right my own definition of average. I'm not sure how to do some things that I think are required though.
define a procedure that takes an arbitrary number of arguments
count those arguments
pass the argument list to (+) to sum them together
Does someone have an example of defining average? I don't seem to know enough about LISP to form a web search that gets back the results I'm looking for.
The definition would be a very simple one-liner, but without spoiling it, you should look into:
a "rest" argument -- this (define (foo . xs) ...xs...) defines foo as a function that takes any number of arguments and they're available as a list which will be the value of xs.
length returns the length of a list.
apply takes a function and a list of values and applies the function to these values.
When you get that, you can go for more:
see the foldl function to avoid applying a list on a potentially very big list (this can matter in some implementations where the length of the argument list is limited, but it wouldn't make much difference in Racket).
note that Racket has exact rationals, and you can use exact->inexact to make a more efficient floating-point version.
And the spoilers are:
(define (average . ns) (/ (apply + ns) (length ns)))
Make it require one argument: (define (average n . ns) (/ (apply + n ns) (add1 (length ns))))
Use foldl: (define (average n . ns) (/ (foldl + 0 (cons n ns)) (add1 (length ns))))
Make it use floating point: (define (average n . ns) (/ (foldl + 0.0 (cons n ns)) (add1 (length ns))))
In Common Lisp, it looks like you can do:
(defun average (&rest args)
(when args
(/ (apply #'+ args) (length args))))
although I have no idea if &rest is available on all implementations of Lisp. Reference here.
Putting that code into GNU CLISP results in:
[1]> (defun average (&rest args)
(when args
(/ (apply #'+ args) (length args))))
AVERAGE
[2]> (average 1 2 3 4 5 6)
7/2
which is 3.5 (correct).
Two versions in Common Lisp:
(defun average (items)
(destructuring-bind (l . s)
(reduce (lambda (c a)
(incf (car c))
(incf (cdr c) a)
c)
items
:initial-value (cons 0 0))
(/ s l)))
(defun average (items &aux (s 0) (l 0))
(dolist (i items (/ s l))
(incf s i)
(incf l)))
In Scheme, I prefer using a list instead of the "rest" argument because rest argument makes implementing procedures like the following difficult:
> (define (call-average . ns)
(average ns))
> (call-average 1 2 3) ;; => BANG!
Packing arbitrary number of arguments into a list allows you to perform any list operation on the arguments. You can do more with less syntax and confusion. Here is my Scheme version of average that take 'n' arguments:
(define (average the-list)
(let loop ((count 0) (sum 0) (args the-list))
(if (not (null? args))
(loop (add1 count) (+ sum (car args)) (cdr args))
(/ sum count))))
Here is the same procedure in Common Lisp:
(defun average (the-list)
(let ((count 0) (sum 0))
(dolist (n the-list)
(incf count)
(incf sum n))
(/ sum count)))
In Scheme R5RS:
(define (average . numbers)
(/ (apply + numbers) (length numbers)))