We need to calculate the "cpu performance per euro" using a function. When we run the code, it returns the error: :: *: expects a number as 1st argument, given (lambda (a1) ...) Why is the argument cpu-clock a lambda function and not a number? How can we correct it? Here is our code (This is not the whole code, but this is the necessary part):
(define-struct cpu (name socket clock cores price))
(define-struct gpu (name gflops memory connectors price))
(define-struct mainboard (name socket connectors price))
;; A list of cpus
(define cpus
(list
(make-cpu 'R9-3900X 'AM4 3.8 12 512.99)
(make-cpu 'R3-1200 'AM4 3.1 4 49.55)
(make-cpu 'R3-3200G 'AM4 3.6 4 92.99)
(make-cpu 'R5-3600 'AM4 3.6 6 195.9)
(make-cpu 'R7-3700X 'AM4 3.6 8 328.9)
(make-cpu 'i3-9100F 'LGA1151v2 3.6 4 75.65)
(make-cpu 'i5-9400F 'LGA1151v2 2.9 6 143.49)
(make-cpu 'i7-9700F 'LGA1151v2 3 8 329.99)
)
)
;; Type:
;; Returns:
(define (cpu-performance-per-euro cpu)
(/ (round (* (/ (* cpu-clock cpu-cores) cpu-price) 1000)) 1000)
)
(check-expect (cpu-performance-per-euro (make-cpu 'R9-3900X 'AM4 3.8 12 512.99)) 0.089)
Thank you in advance.
cpu-clock, cpu-cores, cpu-price, etc., are functions, not values. They take a cpu structure and return the corresponding values. So you should change the function in this way:
(define (cpu-performance-per-euro cpu)
(/ (round (* (/ (* (cpu-clock cpu) (cpu-cores cpu)) (cpu-price cpu)) 1000)) 1000))
Related
I am trying to write a function that takes in the length of a list and a maximum number value and returns a list that is the length given with numbers between 1 and the given max randomly.
so far I have
(define (randomlist n max)
(cond
[(= n 0)empty]
[else
(cons (build-list n (random 1 max))
(randomlist max (- n 1)))]))
I get an error when I run this and was wondering if anybody could help me out.
One can also use for/list to combine loop and list formation:
(define (randomlist n mx)
(for/list ((i n))
(add1 (random mx))))
Testing:
(randomlist 5 10)
Output:
'(5 9 10 4 7)
(random numbers, hence output is very likely to be different each time).
There are several bugs in your code:
It's a bad idea to call a parameter max, that clashes with a built-in procedure. So I renamed it to mx.
There's absolutely no reason to use build-list, that's not how we build an output list, just cons one element with the rest.
random receives zero or one parameters, not two. The single-parameter version returns an integer in the range 0..n-1, hence we have to add 1 to the result to be in the range 1..n.
You switched the order of the parameters when recursively calling randomlist.
This should take care of the problems:
(define (randomlist n mx)
(cond
[(= n 0) empty]
[else
(cons (+ 1 (random mx))
(randomlist (- n 1) mx))]))
It works as expected:
(randomlist 5 10)
=> '(10 7 1 4 8) ; results will vary, obviously
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.
Given the code snippet that follows, is there any meaningful difference between example-func-A and example-func-B?
#lang racket/base
(require (only-in racket/function curry))
(define (((example-func-A x) y) z)
(+ x y z))
(define example-func-B
(curry
(lambda (x y z)
(+ x y z))))
Yes, example-func-A (which uses the MIT-style curried-procedure syntax) is less flexible than example-func-B, in that it expects to only be called with a single argument at a time:
> (((example-func-A 4) 5) 6)
15
> (example-func-A 4 5 6)
example-func-A: arity mismatch;
the expected number of arguments does not match the given number
expected: 1
given: 3
arguments...:
4
5
6
context...:
/opt/homebrew-cask/Caskroom/racket/6.4/Racket v6.4/collects/racket/private/misc.rkt:87:7
In contrast, example-func-B accommodates receiving multiple (or even zero!) arguments:
> (((example-func-B 4) 5) 6)
15
> (example-func-B 4 5 6)
15
> ((((example-func-B) 4)) 5 6)
15
(Presumably the flexibility of curry comes with a bit of a performance hit at runtime.)
I started programming in Common Lisp yesterday. Now I want to find the sum of all the multiples of 3 or 5 below 1000. I came up with:
(loop for n from 1 to 1000 when
(or
(eq (mod n 5) 0)
(eq (mod n 3) 0))
(sum n)))
I know that the looping part works (loop for n from 1 to 1000 sum n) to sum the first 1000 numbers. I know that the ((eq (mod n 5) 0) (mod n 3) 0)) parts works. And I know that (or (eq (mod n 5) 0) (eq (mod n 3) 0)) works. So it looks like a robust program to me, but when I run it I get the error:
1=(SUM N) found where keyword expected getting LOOP clause after WHEN current LOOP context: WHEN (OR (EQ (MOD 1000 5) 0)
(EQ (MOD 1000 3) 0))
1#. [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]
I suspect something is wrong with the (sum n) after the or-statement. But I do not know why that is or how I can solve it. Can someone help me out and get my first Lisp program to work?
sum n, not (sum n)
Don't put sum n in parentheses. The loop macro is its own domain specific language with its own grammar. With it, you'd (loop for ... sum n). The grammar is given in HyperSpec entry on loop in this production:
numeric-accumulation::= {count | counting | sum | summing | }
maximize | maximizing | minimize | minimizing {form | it}
[into simple-var] [type-spec]
If it sounds any better to, you can also write (loop for … summing n). That might read more like a natural English sentence.
=, eql, or zerop, but not eq
It's good practice to get into looking up functions, macros, etc., in the HyperSpec. As Rainer Joswig points out, you shouldn't use eq for comparing numbers. Why? Let's look it up in the HyperSpec. The examples include:
(eq 3 3)
=> true
OR=> false
(eq 3 3.0) => false
(eq 3.0 3.0)
=> true
OR=> false
(eq #c(3 -4) #c(3 -4))
=> true
OR=> false
and the Notes section says (emphasis added):
Objects that appear the same when printed are not necessarily eq to
each other. Symbols that print the same usually are eq to each other
because of the use of the intern function. However, numbers with the
same value need not be eq, and two similar lists are usually not
identical.
An implementation is permitted to make "copies" of characters and
numbers at any time. The effect is that Common Lisp makes no guarantee
that eq is true even when both its arguments are "the same thing" if
that thing is a character or number.
For numbers you need something else. = is a good general numeric comparison, although it does more work here than what you need, because it can compare numbers of different types. E.g., (= 5 5.0) is true. Since you're only concerned about 0, you could use zerop, but that will still do a bit more work than you need, since it will check other numeric types as well. E.g., (zerop #c(0.0 0.0)) is true. In this case, since (mod n …) will be giving you an integer, you can use eql:
The value of eql is true of two objects, x and y, in the folowing
cases:
If x and y are eq.
If x and y are both numbers of the same type and the same value.
If they are both characters that represent the same character.
Thus, you can use (or (eql (mod n 3) 0) (eql (mod n 5) 0)).
Other ways of doing this
Now, your question was about a particular piece of loop syntax, and there were some points to be made about equality operators. However, since some of the other answers have looked at other ways to do this, I think it's worth pointing out that there are much more efficient ways to do this. First, let's look at a way to sum up all the multiples of some number beneath a given limit. E.g., for the number 3 and the inclusive limit 26, we have the sum
?
= 3 + 6 + 9 + 12 + 15 + 18 + 21 + 24
= (3 + 24) + (6 + 21) + (9 + 18) + (12 + 15)
= 27 + 27 + 27 + 27
In general, if you try with a few different numbers, you can work out that for an inclusive limit l and a number n, you'll be adding up pairs of numbers, with an optional half pair if there's a odd number of multiples of n that are less than l. I'm not going to work out the whole derivation, but you can end up with
(defun sum-of-multiples-below-inclusive (limit divisor)
(multiple-value-bind (quotient remainder)
(floor limit divisor)
(let ((pair (+ (- limit remainder) divisor)))
(multiple-value-bind (npairs half-pair)
(floor quotient 2)
(+ (* npairs pair)
(if (oddp half-pair)
(floor pair 2)
0))))))
Then, to find out the sum of the number of multiples less than a given number, you can just substract one from limit:
(defun sum-of-multiples-below (limit divisor)
(sum-of-multiples-below (1- limit) divisor))
Then, to expand to your case, where there are multiple divisors, you'll need to add some of these numbers, and then subtract out the ones that are getting counted twice. E.g., in your case:
(+ (sum-of-multiples-below 1000 3)
(sum-of-multiples-below 1000 5)
(- (sum-of-multiples-below 1000 15)))
;=> 233168
(loop for i from 1 below 1000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i)
;=> 233168
Now, using time naively can lead to misleading results, but SBCL compiles forms before it evaluates them, so this isn't too terrible. This is a very, very, small micro-benchmark, but take a look at the number of cycles used in each form:
(time (+ (sum-of-multiples-below 1000 3)
(sum-of-multiples-below 1000 5)
(- (sum-of-multiples-below 1000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
11,327 processor cycles
0 bytes consed
(time (loop for i from 1 below 1000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
183,843 processor cycles
0 bytes consed
Using the closed form is much faster. The different is more pronounced if we use a higher limit. Let's look at 100,000:
(time (+ (sum-of-multiples-below 100000 3)
(sum-of-multiples-below 100000 5)
(- (sum-of-multiples-below 100000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
13,378 processor cycles
0 bytes consed
(time (loop for i from 1 below 100000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i))
Evaluation took:
0.007 seconds of real time
0.004000 seconds of total run time (0.004000 user, 0.000000 system)
57.14% CPU
18,641,205 processor cycles
0 bytes consed
For 10,000,000 the numbers are even more staggering:
(time (+ (sum-of-multiples-below 10000000 3)
(sum-of-multiples-below 10000000 5)
(- (sum-of-multiples-below 10000000 15))))
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
13,797 processor cycles
0 bytes consed
(time (loop for i from 1 below 10000000
when (or (eql 0 (mod i 3))
(eql 0 (mod i 5)))
sum i))
Evaluation took:
0.712 seconds of real time
0.712044 seconds of total run time (0.712044 user, 0.000000 system)
100.00% CPU
1,916,513,379 processor cycles
0 bytes consed
Some of these Project Euler problems are pretty interesting. Some of them have some pretty straightforward naïve solutions that work for small inputs, but don't scale well at all.
I would format the code like this:
(loop for n from 1 below 1000
when (or (zerop (mod n 3))
(zerop (mod n 5)))
sum n))
one line less
when at the start of a line
no need to have or alone on a line
clauses in LOOP don't have parentheses
use below
This kind of Loop macro goes back to the early 70s to Interlisp, long before Common Lisp existed.
Here is a another solution without loop
(defun sum-to-thousand (count result)
(cond ((> count 1000) result)
((= (mod count 3) 0) (sum-to-thousand (+ count 1) (+ count result)))
((= (mod count 5) 0) (sum-to-thousand (+ count 1) (+ count result)))
(t (sum-to-thousand (+ count 1) result))))
May I propose more "lispier" variant:
CL-USER> (defun my-sum (&key (from 1) to dividers (sum 0))
(if (>= from to)
sum
(my-sum :from (1+ from)
:to to
:dividers dividers
:sum (if (some (lambda (x) (zerop (mod from x))) dividers)
(+ sum from)
sum))))
MY-SUM
CL-USER> (my-sum :to 1000 :dividers '(3 5))
233168
I'm looking for something like #'delete-duplicates, but I know that all elements of the list are already sorted, or inversely sorted, or at least arranged so that duplicates will already be adjacent to each other. I wish to use that knowledge to ensure that execution speed is not proporational to the square of the number of elements in the list. It's trivial to use #'maplist to grow my own solution, but is there something already in the language? It would be embarrassing to reinvent the wheel.
To be clear, for largish lengths of lists, I would like the running time of the deletion to be proportional to the length of the list, not proportional to the square of that length. This is the behavior I wish to avoid:
1 (defun one-shot (cardinality)
2 (labels ((generate-list (the-count)
3 (let* ((the-list (make-list the-count)))
4 (do ((iterator 0 (1+ iterator)))
5 ((>= iterator the-count))
6 (setf (nth iterator the-list) iterator))
7 the-list)))
8 (let* ((given-list (generate-list cardinality))
9 (stripped-list)
10 (start-time)
11 (end-time))
12 (setf start-time (get-universal-time))
13 (setf stripped-list (delete-duplicates given-list :test #'eql))
14 (setf end-time (get-universal-time))
15 (princ "for n = ")
16 (princ cardinality)
17 (princ ", #'delete-duplicates took ")
18 (princ (- end-time start-time))
19 (princ " seconds")
20 (terpri))))
21 (one-shot 20000)
22 (one-shot 40000)
23 (one-shot 80000)
for n = 20000, #'delete-duplicates took 6 seconds
for n = 40000, #'delete-duplicates took 24 seconds
for n = 80000, #'delete-duplicates took 95 seconds
There's nothing like this in the language, but something like this makes just one pass through the list:
(defun delete-adjacent-duplicates (list &key key (test 'eql))
(loop
for head = list then (cdr head)
until (endp head)
finally (return list)
do (setf (cdr head)
(member (if (null key) (car head)
(funcall key (car head)))
(cdr head)
:key key :test-not test))))
As, #wvxvw pointed out, it might be possible to simplify this iteration using (loop for head on list finally (return list) do ...). However, 3.6 Traversal Rules and Side Effects says that modifying the cdr chain of a list during an object-traversal leads to undefined behavior. However, it's not clear whether loop for head on list is technically an object-traversal operation or not. The documentation about loop says in 6.1.2.1.3 The for-as-on-list subclause that
In the for-as-on-list subclause, the for or as construct iterates over
a list. … The
variable var is bound to the successive tails of the list in form1. At
the end of each iteration, the function step-fun is applied to the
list; the default value for step-fun is cdr. … The for or as construct
causes termination when the end of the list is reached.
This says that the step function is always applied at the end of the iteration, so it sounds like loop for head on list should be OK. At any rate, any possible issues could be avoided by using do loop instead:
(defun delete-adjacent-duplicates (list &key key (test 'eql))
(do ((head list (cdr head)))
((endp head) list)
(setf (cdr head)
(member (if (null key) (car head)
(funcall key (car head)))
(cdr head)
:key key :test-not test))))
The idea is to start with head being the list, then setting its cdr to the first tail that starts with a different element, then advancing the head, and continuing until there's nothing left. This should be linear in the length of the list, assuming that member is implemented in a sensible way. The use of member means that you don't have to do any extra work to get :key and :test working in the appropriate way. (Do note that :test for del-dups is going to be the :test-not of member.) Note: there's actually a slight issue with this, in that the key function will called twice for each element in the final list: once when it's the first element of a tail, and once when it's the car of head.
CL-USER> (delete-adjacent-duplicates (list 1 1 1 1 2 2 3 3 3))
(1 2 3)
CL-USER> (delete-adjacent-duplicates (list 1 2 2))
(1 2)
CL-USER> (delete-adjacent-duplicates (list 1 3 5 6 4 2 3 5) :key 'evenp)
(1 6 3)
I expect that any linear time solution is going to take a similar approach; hold a reference to the current head, find the next tail that begins with a different element, and then make that tail the cdr of the head.
I would expect REMOVE-DUPLICATES to have a linear time implementation. (And indeed it does* on my local SBCL install.)
Note that REMOVE-DUPLICATES and DELETE-DUPLICATES are specified to have the same return value, and that the side effects of DELETE-DUPLICATES are not guaranteed.
* The linear time code path is only taken when the :test is #'eq,#'eql, #'equal, or #'equalp (it relies on a hash table) and there is no :key or :test-not argument supplied.
For the record: your test code is basically just this:
(defun one-shot (n &aux (list (loop for i below n collect i)))
(time (delete-duplicates list))
(values))
It might also be useful to talk to the implementation maintainers in the case of a slow delete-duplicates.
For example (one-shot 1000000) runs in a second in CCL on my Mac. In LispWorks it runs in 0.155 seconds.
There is nothing like that in the language standard. However, you can do that either with a loop:
(defun remove-adjacent-duplicates (list &key (test #'eql))
(loop for obj in list
and prev = nil then obj
for take = t then (not (funcall test obj prev))
when take collect obj))
or with reduce (exercise left to the reader).
See the other answer for a destructive implementation.
PS. Unless you are doing something tricky with timing, you are much better off using time.
A bit different approach:
(defun compress-duplicates (list &key (test #'eql))
(labels ((%compress-duplicates (head tail)
(if (null tail)
(setf (cdr head) tail)
(progn (unless (funcall test (car head) (car tail))
(setf (cdr head) tail head (cdr head)))
(%compress-duplicates head (cdr tail))))))
(%compress-duplicates list (cdr list))
list))
(compress-duplicates (list 1 1 1 2 2 3 4 4 1 1 1))
;; (1 2 3 4 1)
Test of SBCL delete-duplicates implementation:
(defun test-delete-duplicates ()
(labels ((%test (list)
(gc)
(time (delete-duplicates list))))
(loop
:repeat 6
:for list := (loop :for i :from 0 :below 1000
:collect (random 100))
:then (append list list) :do (%test (copy-list list)))))
;; (test-delete-duplicates)
;; Evaluation took:
;; 0.002 seconds of real time
;; 0.002000 seconds of total run time (0.002000 user, 0.000000 system)
;; 100.00% CPU
;; 3,103,936 processor cycles
;; 0 bytes consed
;; Evaluation took:
;; 0.003 seconds of real time
;; 0.003000 seconds of total run time (0.003000 user, 0.000000 system)
;; 100.00% CPU
;; 6,347,431 processor cycles
;; 0 bytes consed
;; Evaluation took:
;; 0.006 seconds of real time
;; 0.006000 seconds of total run time (0.005000 user, 0.001000 system)
;; 100.00% CPU
;; 12,909,947 processor cycles
;; 0 bytes consed
;; Evaluation took:
;; 0.012 seconds of real time
;; 0.012000 seconds of total run time (0.012000 user, 0.000000 system)
;; 100.00% CPU
;; 25,253,024 processor cycles
;; 0 bytes consed
;; Evaluation took:
;; 0.023 seconds of real time
;; 0.022000 seconds of total run time (0.022000 user, 0.000000 system)
;; 95.65% CPU
;; 50,716,442 processor cycles
;; 0 bytes consed
;; Evaluation took:
;; 0.049 seconds of real time
;; 0.050000 seconds of total run time (0.050000 user, 0.000000 system)
;; 102.04% CPU
;; 106,747,876 processor cycles
;; 0 bytes consed
Shows linear speed.
Test of ECL delete-duplicates implementation:
;; (test-delete-duplicates)
;; real time : 0.003 secs
;; run time : 0.003 secs
;; gc count : 1 times
;; consed : 95796160 bytes
;; real time : 0.007 secs
;; run time : 0.006 secs
;; gc count : 1 times
;; consed : 95874304 bytes
;; real time : 0.014 secs
;; run time : 0.014 secs
;; gc count : 1 times
;; consed : 95989920 bytes
;; real time : 0.028 secs
;; run time : 0.027 secs
;; gc count : 1 times
;; consed : 96207136 bytes
;; real time : 0.058 secs
;; run time : 0.058 secs
;; gc count : 1 times
;; consed : 96617536 bytes
;; real time : 0.120 secs
;; run time : 0.120 secs
;; gc count : 1 times
;; consed : 97412352 bytes
Linear time increase too.