I am trying to write a simple coin flip program in Common Lisp. This is the code I have
(defun yn
(let ht (random 1)
(if (eq ht 1)
(princ heads)
(princ tails))
)
)
It seems simple enough, but I keep getting the error:
"Invalid specialized parameter in method lambda list (LET HT (RANDOM 1) (IF (EQ HT 1) (PRINC HEADS) (PRINC TAILS))): (IF (EQ HT 1) (PRINC HEADS) (PRINC TAILS))"
)
What could be wrong here?
For defun without parameters, there should be an empty parameter list, like the following:
(defun yn ()
(let ((ht (random 2)))
(if (eq ht 1)
(princ heads)
(princ tails))))
Your defun is malformed (lacking an empty parameter list).
Your let is malformed (the general structure is (let (<bind-spec>...) <body>), where the binding is either a symbol (initially bound to nil) or a (<symbol> <value>) list.
Both heads and tails seem to be unbound, it's not clear if you mean the literal symbols or are using dynamically-scoped variables.
Your call to random probably doesn't do what you want, the HyperSpec says "Returns a pseudo-random number that is a non-negative number less than limit and of the same type as limit. ", so (random 1) can only return 0.
Below is a cleaned-up version that addresses all of these points, assuming you're after having the literal symbols heads and tails printed.
(defun yn ()
(let ((ht (random 2)))
(if (eq ht 1)
(princ 'heads)
(princ 'tails)))
Related
contextualization: I've been doing a university project in which I have to write a parser for regular expressions and build the corresponding epsilon-NFA. I have to do this in Prolog and Lisp.
I don't know if questions like this are allowed, if not I apologize.
I heard some of my classmates talking about how they used the function gensym for that, I asked them what it did and even checked up online but I literally can't understand what this function does neither why or when is best to use it.
In particular, I'm more intrested in what it does in Lisp.
Thank you all.
GENSYM creates unique symbols. Each call creates a new symbol. The symbol usually has a name which includes a number, which is counted up. The name is also unique (the symbol itself is already unique) with a number, so that a human reader can identify different uninterned symbols in the source code.
CL-USER 39 > (gensym)
#:G1083
CL-USER 40 > (gensym)
#:G1084
CL-USER 41 > (gensym)
#:G1085
CL-USER 42 > (gensym)
#:G1086
gensym is often used in Lisp macros for code generation, when the macro needs to create new identifiers, which then don't clash with existing identifiers.
Example: we are going to double the result of a Lisp form and we are making sure that the Lisp form itself will be computed only once. We do that by saving the value in a local variable. The identifier for the local variable will be computed by gensym.
CL-USER 43 > (defmacro double-it (it)
(let ((new-identifier (gensym)))
`(let ((,new-identifier ,it))
(+ ,new-identifier ,new-identifier))))
DOUBLE-IT
CL-USER 44 > (macroexpand-1 '(double-it (cos 1.4)))
(LET ((#:G1091 (COS 1.4)))
(+ #:G1091 #:G1091))
T
CL-USER 45 > (double-it (cos 1.4))
0.33993432
a little clarification of the existing answers (as the op is not yet aware of the typical common lisp macros workflow):
consider the macro double-it, proposed by mr. Joswig. Why would we bother creating this whole bunch of let? when it can be simply:
(defmacro double-it (it)
`(+ ,it ,it))
and ok, it seems to be working:
CL-USER> (double-it 1)
;;=> 2
but look at this, we want to increment x and double it
CL-USER> (let ((x 1))
(double-it (incf x)))
;;=> 5
;; WHAT? it should be 4!
the reason can be seen in macro expansion:
(let ((x 1))
(+ (setq x (+ 1 x)) (setq x (+ 1 x))))
you see, as the macro doesn't evaluate form, just splices it into generated code, it leads to incf being executed twice.
the simple solution is to bind it somewhere, and then double the result:
(defmacro double-it (it)
`(let ((x ,it))
(+ x x)))
CL-USER> (let ((x 1))
(double-it (incf x)))
;;=> 4
;; NICE!
it seems to be ok now. really it expands like this:
(let ((x 1))
(let ((x (setq x (+ 1 x))))
(+ x x)))
ok, so what about the gensym thing?
let's say, you want to print some message, before doubling your value:
(defmacro double-it (it)
`(let* ((v "DOUBLING IT")
(val ,it))
(princ v)
(+ val val)))
CL-USER> (let ((x 1))
(double-it (incf x)))
;;=> DOUBLING IT
;;=> 4
;; still ok!
but what if you accidentally name value v instead of x:
CL-USER> (let ((v 1))
(double-it (incf v)))
;;Value of V in (+ 1 V) is "DOUBLING IT", not a NUMBER.
;; [Condition of type SIMPLE-TYPE-ERROR]
It throws this weird error! Look at the expansion:
(let ((v 1))
(let* ((v "DOUBLING IT") (val (setq v (+ 1 v))))
(princ v)
(+ val val)))
it shadows the v from the outer scope with string, and when you are trying to add 1, well it obviously can't. Too bad.
another example, say you want to call the function twice, and return 2 results as a list:
(defmacro two-funcalls (f v)
`(let ((x ,f))
(list (funcall x ,v) (funcall x ,v))))
CL-USER> (let ((y 10))
(two-funcalls (lambda (z) z) y))
;;=> (10 10)
;; OK
CL-USER> (let ((x 10))
(two-funcalls (lambda (z) z) x))
;; (#<FUNCTION (LAMBDA (Z)) {52D2D4AB}> #<FUNCTION (LAMBDA (Z)) {52D2D4AB}>)
;; NOT OK!
this class of bugs is very nasty, since you can't easily say what's happened.
What is the solution? Obviously not to name the value v inside macro. You need to generate some sophisticated name that no one would reproduce in their code, like my-super-unique-value-identifier-2019-12-27. This would probably save you, but still you can't really be sure. That's why gensym is there:
(defmacro two-funcalls (f v)
(let ((fname (gensym)))
`(let ((,fname ,f))
(list (funcall ,fname ,v) (funcall ,fname ,v)))))
expanding to:
(let ((y 10))
(let ((#:g654 (lambda (z) z)))
(list (funcall #:g654 y) (funcall #:g654 y))))
you just generate the var name for the generated code, it is guaranteed to be unique (meaning no two gensym calls would generate the same name for the runtime session),
(loop repeat 3 collect (gensym))
;;=> (#:G645 #:G646 #:G647)
it still can potentially be clashed with user var somehow, but everybody knows about the naming and doesn't call the var #:GXXXX, so you can consider it to be impossible. You can further secure it, adding prefix
(loop repeat 3 collect (gensym "MY_GUID"))
;;=> (#:MY_GUID651 #:MY_GUID652 #:MY_GUID653)
GENSYM will generate a new symbol at each call. It will be garanteed, that the symbol did not exist before it will be generated and that it will never be generated again. You may specify a symbols prefix, if you like:
CL-USER> (gensym)
#:G736
CL-USER> (gensym "SOMETHING")
#:SOMETHING737
The most common use of GENSYM is generating names for items to avoid name clashes in macro expansion.
Another common purpose is the generaton of symbols for the construction of graphs, if the only thing demand you have is to attach a property list to them, while the name of the node is not of interest.
I think, the task of NFA-generation could make good use of the second purpose.
This is a note to some of the other answers, which I think are fine. While gensym is the traditional way of making new symbols, in fact there is another way which works perfectly well and is often better I find: make-symbol:
make-symbol creates and returns a fresh, uninterned symbol whose name is the given name. The new-symbol is neither bound nor fbound and has a null property list.
So, the nice thing about make-symbol is it makes a symbol with the name you asked for, exactly, without any weird numerical suffix. This can be helpful when writing macros because it makes the macroexpansion more readable. Consider this simple list-collection macro:
(defmacro collecting (&body forms)
(let ((resultsn (make-symbol "RESULTS"))
(rtailn (make-symbol "RTAIL")))
`(let ((,resultsn '())
(,rtailn nil))
(flet ((collect (it)
(let ((new (list it)))
(if (null ,rtailn)
(setf ,resultsn new
,rtailn new)
(setf (cdr ,rtailn) new
,rtailn new)))
it))
,#forms
,resultsn))))
This needs two bindings which the body can't refer to, for the results, and the last cons of the results. It also introduces a function in a way which is intentionally 'unhygienic': inside collecting, collect means 'collect something'.
So now
> (collecting (collect 1) (collect 2) 3)
(1 2)
as we want, and we can look at the macroexpansion to see that the introduced bindings have names which make some kind of sense:
> (macroexpand '(collecting (collect 1)))
(let ((#:results 'nil) (#:rtail nil))
(flet ((collect (it)
(let ((new (list it)))
(if (null #:rtail)
(setf #:results new #:rtail new)
(setf (cdr #:rtail) new #:rtail new)))
it))
(collect 1)
#:results))
t
And we can persuade the Lisp printer to tell us that in fact all these uninterned symbols are the same:
> (let ((*print-circle* t))
(pprint (macroexpand '(collecting (collect 1)))))
(let ((#2=#:results 'nil) (#1=#:rtail nil))
(flet ((collect (it)
(let ((new (list it)))
(if (null #1#)
(setf #2# new #1# new)
(setf (cdr #1#) new #1# new)))
it))
(collect 1)
#2#))
So, for writing macros I generally find make-symbol more useful than gensym. For writing things where I just need a symbol as an object, such as naming a node in some structure, then gensym is probably more useful. Finally note that gensym can be implemented in terms of make-symbol:
(defun my-gensym (&optional (thing "G"))
;; I think this is GENSYM
(check-type thing (or string (integer 0)))
(let ((prefix (typecase thing
(string thing)
(t "G")))
(count (typecase thing
((integer 0) thing)
(t (prog1 *gensym-counter*
(incf *gensym-counter*))))))
(make-symbol (format nil "~A~D" prefix count))))
(This may be buggy.)
I am working on problem to get the occurence of Prime in a list in lisp.
Input:
Write a function (OccurencesOfPrimes < list >) which counts the number of primes in a (possibly nested) list.
Output: Example: (OccurencesOfPrimes (((1)(2))(5)(3)((8)3)) returns 4.
I am using the below code but getting the error like:
(
defun OccurencesOfPrimes (list)
(loop for i from 2 to 100
do ( setq isPrime t)
(loop for j from 2 to i
never (zerop (mod i j))
(setq isPrime f)
(break)
)
)
(if (setq isPrime t)
(append list i)
)
)
)
LOOP: illegal syntax near (SETQ ISPRIME F) in
(LOOP FOR J FROM 2 TO I NEVER (ZEROP (MOD I J)) (SETQ ISPRIME F) (BREAK)
)
Any help.
It is important to keep the format consistent with the expected conventions of the language. It helps when reading the code (in particular with other programmers), and can help you see errors.
Also, you should use an editor which, at the minimum, keep tracks of parentheses. In Emacs, when you put the cursor in the first opening parenthesis, the matching parenthesis is highlighted. You can spot that you have one additional parenthesis that serves no purpose.
(
defun OccurencesOfPrimes (list)
(loop for i from 2 to 100
do ( setq isPrime t)
(loop for j from 2 to i
never (zerop (mod i j))
(setq isPrime f)
(break)
)
)
(if (setq isPrime t)
(append list i)
)
) ;; <- end of defun
) ;; <- closes nothing
In Lisp, parentheses are for the computer, whereas indentation is for humans. Tools can automatically indent the code according to the structure (the parenthesis), and any discrepancy between what indentation you expect and the one being computed is a hint that your code is badly formed. If you look at the indentation of your expressions, you can see how deep you are in the form, and that alone helps you understand the code.
Symbol names are dash-separated, not camlCased.
Your code, with remarks:
(defun occurences-of-primes (list)
;; You argument is likely to be a LIST, given its name and the way
;; you call APPEND below. But you never iterate over the list. This
;; is suspicious.
(loop
for i from 2 to 100
do
(setq is-prime t) ;; setting an undeclared variable
(loop
for j from 2 to i
never (zerop (mod i j))
;; the following two forms are not expected here according
;; to LOOP's grammar; setting IS-PRIME to F, but F is not
;; an existing variable. If you want to set to false, use
;; NIL instead.
(setq is-prime f)
;; BREAK enters the debugger, maybe you wanted to use
;; LOOP-FINISH instead, but the NEVER clause above should
;; already be enough to exit the loop as soon as its
;; sub-expression evaluates to NIL.
(break)))
;; The return value of (SETQ X V) is V, so here your test would
;; always succeed.
(if (setq is-prime t)
;; Append RETURNS a new list, without modifying its
;; arguments. In particular, LIST is not modified. Note that "I"
;; is unknown at this point, because the bindings effective
;; inside the LOOP are not visible in this scope. Besides, "I"
;; is a number, not a list.
(append list i)))
Original question
Write one function which counts all the occurrences of a prime number in a (possibly nested) list.
Even though the homework questions says "write one function", it does not say that you should write one big function that compute everything at once. You could write one such big function, but if you split your problem into sub-problems, you will end with different auxiliary functions, which:
are simpler to understand (they do one thing)
can be reused to build other functions
The sub-problems are, for example: how to determine if a number is a prime? how to iterate over a tree (a.k.a. a possibly nested list)? how to count
the occurrences?
The basic idea is to write an "is-prime" function, iterate over the tree and call "is-prime" on each element; if the element is prime and was never seen before, add 1 to a counter, local to your function.
You can also flatten the input tree, to obtain a list, then sort the resulting
list; you iterate over the list while keeping track of the last
value seen: if the value is the same as the previous one, you
already know if the number is prime; if the previous number differs, then
you have to test if the number is prime first.
You could also abstract things a little more, and define a higher-order tree-walker function, which calls a function on each leaf of the tree. And write another higher-order function which "memoizes" calls: it wraps around a
function F so that if you call F with the same arguments as before,
it returns the result that was stored instead of recomputing it.
Example
I'll combine the above ideas because if you give that answer to a teacher you are likely to have to carefully explain what each part does (and if you can, great for you); this is not necessarily the "best" answer, but it covers a lot of things.
(defun tree-walk-leaves (tree function)
(typecase tree
(null nil)
(cons
(tree-walk-leaves (car tree) function)
(tree-walk-leaves (cdr tree) function))
(t (funcall function tree))))
(defun flatten (tree &optional keep-order-p)
(let ((flat nil))
(tree-walk-leaves tree (lambda (leaf) (push leaf flat)))
(if keep-order-p
(nreverse flat)
flat)))
(defun prime-p (n)
(or (= n 2)
(and (> n 2)
(oddp n)
(loop
for d from 3 upto (isqrt n) by 2
never (zerop (mod n d))))))
(defun count-occurences-of-prime (tree)
(count-if #'prime-p (remove-duplicates (flatten tree))))
(count-occurences-of-prime '(((1)(2))(5)(3)((8)3)))
=> 4
If, instead, you don't want to remove duplicates but count the multiple times a prime number occurs, you can do:
(count-if (memoize #'prime-p) (flatten tree))
... where memoize is:
(defun memoize (function &key (test #'equalp) (key #'identity))
(let ((hash (make-hash-table :test test)))
(lambda (&rest args)
(let ((args (funcall key args)))
(multiple-value-bind (result exists-p) (gethash args hash)
(values-list
(if exists-p
result
(setf (gethash args hash)
(multiple-value-list (apply function args))))))))))
(memoize is useless if there are no duplicates)
I have the following setup in Common Lisp. my-object is a list of 5 binary trees.
(defun make-my-object ()
(loop for i from 0 to 5
for nde = (init-tree)
collect nde))
Each binary tree is a list of size 3 with a node, a left child and a right child
(defstruct node
(min 0)
(max 0)
(ctr 0))
(defun vals (tree)
(car tree))
(defun left-branch (tree)
(cadr tree))
(defun right-branch (tree)
(caddr tree))
(defun make-tree (vals left right)
(list vals left right))
(defun init-tree (&key (min 0) (max 1))
(let ((n (make-node :min min :max max)))
(make-tree n '() '())))
Now, I was trying to add an element to one of the binary trees manually, like this:
(defparameter my-object (make-my-object))
(print (left-branch (car my-object))) ;; returns NIL
(let ((x (left-branch (car my-object))))
(setf x (cons (init-tree) x)))
(print (left-branch (car my-object))) ;; still returns NIL
The second call to print still returns NIL. Why is this? How can I add an element to the binary tree?
The first function is just:
(defun make-my-object ()
(loop repeat 5 collect (init-tree)))
Now you define a structure for node, but you use a list for the tree and my-object? Why aren't they structures?
Instead of car, cadr and caddr one would use first, second, third.
(let ((x (left-branch (car my-object))))
(setf x (cons (init-tree) x)))
You set the local variable x to a new value. Why? After the let the local variable is also gone. Why aren't you setting the left branch instead? You would need to define a way to do so. Remember: Lisp functions return values, not memory locations you can later set. How can you change the contents in a list? Even better: use structures and change the slot value. The structure (or even CLOS classes) has following advantages over plain lists: objects carry a type, slots are named, accessors are created, a make function is created, a type predicate is created, ...
Anyway, I would define structures or CLOS classes for node, tree and object...
Most of the code in this question isn't essential to the real problem here. The real problem comes in with the misunderstanding of this code:
(let ((x (left-branch (car my-object))))
(setf x (cons (init-tree) x)))
We can see the same kind of behavior without user-defined structures of any kind:
(let ((cell (cons 1 2)))
(print cell) ; prints (1 . 2)
(let ((x (car cell)))
(setf x 3)
(print cell))) ; prints (1 . 2)
If you understand why both print statements produce (1 . 2), then you've got enough to understand why your own code isn't doing what you (previously) expected it to do.
There are two variables in play here: cell and x. There are three values that we're concerned with 1, 2, and the cons-cell produced by the call (cons 1 2). Variables in Lisp are often called bindings; the variable, or name, is bound to a value. The variable cell is bound to the the cons cell (1 . 2). When we go into the inner let, we evaluate (car cell) to produce the value 1, which is then bound to the variable x. Then, we assign a new value, 3, to the variable x. That doesn't modify the cons cell that contains the value that x was originally bound to. Indeed, the value that was originally bound to x was produced by (car cell), and once the call to (car cell) returned, the only value that mattered was 1.
If you have some experience in other programming languages, this is directly analogous to something like
int[] array = ...;
int x = array[2]; // read from the array; assign result to x
x = 42; // doesn't modify the array
If you want to modify a structure, you need to setf the appropriate part of the structure. E.g.:
(let ((cell (cons 1 2)))
(print cell) ; prints (1 . 2)
(setf (car cell) 3)
(print cell)) ; prints (3 . 2)
The code below server to show the number of integer in a list.
(defun isNum (N)
(and (<= N 9) (>= N 0)))
(defun count-numbers (list)
(let ((count 0))
(dolist (item list count)
(cond
((null list) nil)
(((and (<= N 9) (>= N 0))) item)(incf count))
(setq(0 + count))))))
I get the error A' is not of the expected typeREAL' when I run the command
(count-numbers '(3 4 5 6 a 7 b) )
I'm surprised it runs at all, given that your cond is improperly constructed, you switch to infix notation in the unnecessarily side-effect-generating bit of your code and you're using unbound variables in count-numbers. Hypothetically, if it did run, that error sounds about right. You're doing numeric comparisons on a parameter (and those error on non-numeric input).
I've got my codereview hat on today, so lets go through this a bit more in-depth.
Lisp (it actually doesn't matter which, afaik this applies to CL, Scheme and all the mongrels) uses lower-case-snake-case-with-dashes, and not lowerCamelCase for variable and function names.
(defun is-num (n)
(and (<= n 9) (>= n 0)))
Common Lisp convention is to end a predicate with p or -p rather than begin them with is-. Scheme has the (IMO better) convention of ending predicates with ? instead
(defun num-p (n)
(and (<= n 9) (>= n 0)))
((and (<= N 9) (>= N 0))) is not how you call a function. You actually need to use its name, not just attempt to call its body. This is the source of one of the many errors you'd get if you tried to run this code.
(defun count-numbers (list)
(let ((count 0))
(dolist (item list count)
(cond
((null list) nil)
((num-p item) item)(incf count))
(setq(0 + count))))))
numberp already exists, and does a type check on its input rather than attempting numeric comparisons. You should probably use that instead.
(defun count-numbers (list)
(let ((count 0))
(dolist (item list count)
(cond
((null list) nil)
((numberp item) item)(incf count))
(setq(0 + count))))))
((numberp item) item) (incf count)) probably doesn't do what you think it does as a cond clause. It actually gets treated as two separate clauses; one checks whether item is a number, and returns it if it is. The second tries to check the variable incf and returns count if it evaluates to t (which it doesn't, and won't). What you seem to want is to increment the counter count when you find a number in your list, which means you should put that incf clause in with the item.
(defun count-numbers (list)
(let ((count 0))
(dolist (item list count)
(cond ((null list) nil)
((numberp item)
(incf count)
item))
(setq (0 + count)))))
(setq (0 + count)) is the wrong thing for three reasons
You seem to have tripped back into infix notation, which means that the second bit there is actually attempting to call the function 0 with the variables + and count as arguments.
You don't have a second part to the setq, which means you're trying to set the above to NIL implicitly.
You don't actually need to set anything in order to return a value
At this point, we finally have a piece of code that will evaluate and run properly (and it doesn't throw the error you mention above).
(defun count-numbers (list)
(let ((count 0))
(dolist (item list count)
(cond ((null list) nil)
((numberp item)
(incf count)
item))
count)))
dolist is an iteration construct that does something for each element in a given list. That means you don't actually need to test for list termination manually with that cond. Also, because dolist doesn't collect results, there's no reason to return item to it. You're also unnecessarily shadowing the local count you declare in the let.
(defun count-numbers (list)
(let ((count 0))
(dolist (item list)
(when (numberp item) (incf count)))
count))
As usual, you can do all this with a simpler loop call.
(defun count-numbers (list)
(loop for item in list
when (numberp item) sum 1))
which makes the counter implicit and saves you from needing to return it manually. In fact, unless this was specifically an exercise to write your own iteration function, Common Lisp has a built in count-if, which takes predicate sequence [some other options] and returns the count of items in sequence that match predicate. If you wanted to name count-numbers specifically, for stylistic reasons, you could just
(defun count-numbers (list) (count-if #'numberp list))
and be done with it.
In conclusion, good try, but please try reading up on the language family for realzies before asking further questions.
Yet another way to do it would be:
(reduce
#'(lambda (a b)
(if (numberp b) (1+ a) a))
'(3 4 5 6 a 7 b) :initial-value 0) ; 5
I.e. process the sequence in a way that you are given at each iteration the result of the previous iteration + the next member of the sequence. Start with zero and increment the result each time the element in the sequence is a number.
EDIT
Sorry, I haven't seen Inaimathi mentioned count-if. That would be probably better.
It seems that I have to make it in detail; it's my homework. I don't
want to copy the code written by you. I'm a newbie; what I'm trying
to learn is how to decompose a subject to single pieces, and then
focus on what function should I use to solve the problem. It's a
little hard to finish these problems by my own, because I'm completely
a newbie in Lisp, actually in how to program. I hope you can help me
out.
Here is the problem: there is a given constant
(defconstant *storms* '((bob 65)
(chary 150)
(jenny 145)
(ivan 165)
(james 120)))
Each storm is represented by a list of its name and its wind speed.
The wind speeds are to be categorized as follows:
39–74 → tropical
75–95 → cat-1
96–110 → cat-2
111–130 → cat-3
131–155 → cat-4
156 or more → cat-5
Now I have to write two functions:
storm-categories should generate category names, like this: (bob
tropical), (chary cat-1), …
and storm-distribution should generate the number of storms in
each category, like this: (cat-1 1), (cat-2 0), …
The way I try to solve this problem is:
Use if statements to judge the type of windspeed:
(if (and (> x 39) (< x 73)) (print 'tropical))
(if (and (> x 74) (< x 95)) (print 'cat-1))
(if (and (> x 96) (< x 110)) (print 'cat-2))
(if (and (> x 111) (< x 130)) (print'cat-3))
(if (and (> x 131) (< x 155)) (print'cat-4))
(if (and (> x 156)) (print 'cat-5))
Replace the windspeed (like 65) with windtype (like cat-1)
(loop for x in storms
do (rplacd x ‘windtype)
I just have a simple idea of the first function, but still don't know
how to implement it. I haven't touched the distribution function,
because I am still stuck on the first one.
DEFCONSTANT is wrong. It makes no sense to make your input a constant. A variable defined with DEFVAR or DEFPARAMETER is fine.
Instead of IF use COND. COND allows the testing of several conditions.
You don't want to use PRINT. Why print something. You want to compute a value.
RPLACA is also wrong. That's used for destructive modification. You don't want that. You want to create a new value. Something like RPLACA might be used in the function DISTRIBUTION (see below).
Use functional abstraction. Which functions are useful?
BETWEEN-P, is a value X between a and b ?
STORM-CATEGORY, for a given wind speed return the category
STORM-CATEGORIES, for a list of items (storm wind-speed) return a list of items (storm category). Map over the input list to create the result list.
DISTRIBUTION, for a list of items (tag category) return a list with items (category number-of-tags-in-this-category).
STORM-DISTRIBUTION, for a list of items (storm category) return a list with items (category number-of-storms-in-this-category). This basically calls DISTRIBUTION with the right parameters.
The function DISTRIBUTION is the most complicated of the above. Typically one would use a hashtable or a assoc list as an intermediate help to keep a count of the occurrences. Map over the input list and update the corresponding count.
Also: a good introduction into basic Lisp is the book Common Lisp: A Gentle Introduction to Symbolic Computation - it is freely available as a PDF for download. A more fun and also basic introduction to Lisp is the book Land of Lisp.
Okay roccia, you have posted your answer. Here comes mine hacked in a few minutes, but it should give you some ideas:
First let's start with the data:
(defparameter *storms2004*
'((BONNIE 65)
(CHARLEY 150)
(FRANCES 145)
(IVAN 165)
(JEANNE 120)))
(defparameter *storm-categories*
'((39 73 tropical-storm)
(74 95 hurricane-cat-1)
(96 110 hurricane-cat-2)
(111 130 hurricane-cat-3)
(131 155 hurricane-cat-4)
(156 nil hurricane-cat-5)))
A function that checks if a value is between two bounds. If the right bound can also be missing (NIL).
(defun between (value a b)
(<= a value (if b b value)))
Note that Lisp allows the comparison predicate with more than two arguments.
Let's find the category of a storm. The Lisp functions FIND and FIND-IF find things in lists.
(defun storm-category (storm-speed)
(third (find-if (lambda (storm)
(between storm-speed (first storm) (second storm)))
*storm-categories*)))
Let's compute the category for each storm. Since we get a list of (storm wind-speed), we just map over the function which computes the category over the list. We need to return a list of storms and category.
(defun storm-categories (list)
(mapcar (lambda (storm)
(list (first storm)
(storm-category (second storm))))
list))
Now we take the the same list of storms, but use a hash table to keep track of how many storms there were in each category. MAPC is like MAPCAR, but only for the side effect of updating the hash table. ÌNCF increments the count. When we have filled the hash table, we need to map over it with MAPHASH. For each pair of key and value in the table, we just push the pair onto a result list and then we are returning that result.
(defun storm-distribution (storms)
(let ((table (make-hash-table)))
(mapc (lambda (storm)
(incf (gethash (second storm) table 0)))
(storm-categories storms))
(let ((result nil))
(maphash (lambda (key value)
(push (list key value) result))
table)
result)))
Test:
CL-USER 33 > (storm-category 100)
HURRICANE-CAT-2
CL-USER 34 > (storm-categories *storms2004*)
((BONNIE TROPICAL-STORM)
(CHARLEY HURRICANE-CAT-4)
(FRANCES HURRICANE-CAT-4)
(IVAN HURRICANE-CAT-5)
(JEANNE HURRICANE-CAT-3))
CL-USER 35 > (storm-distribution *storms2004*)
((HURRICANE-CAT-5 1)
(HURRICANE-CAT-4 2)
(HURRICANE-CAT-3 1)
(TROPICAL-STORM 1))
Looks fine to me.
Finally finished this problem. the second part is really makes me crazy. I cant't figure out how to use hashtable or assoc list to slove it. Anyway the assignment is done, but I want to know how can I simplify it... Hope u guys can help me . Thanks for your help Joswing, your idea really helps me a lot...
(defconstant *storms2004* '((BONNIE 65)(CHARLEY 150)(FRANCES 145)(IVAN 165)(JEANNE 120)))
(defun storm-category (x) ; for given windspeed return the category
(cond
((and (> x 39) (< x 73) 'tropical-storm))
((and (> x 74) (< x 95) 'hurricane-cat-1))
((and (> x 96) (< x 110) 'hurricane-cat-2))
((and (> x 111) (< x 130) 'hurricane-cat-3))
((and (> x 131) (< x 155) 'hurricane-cat-4))
( t 'hurricane-cat-5)
)
);end storm-category
(defun storm-categories (lst) ;for a list of storm and windspeed return storm's name and wind type
(let ((result nil))
(dolist (x lst (reverse result)) ;
(push
(list (first x) (storm-category (second x)) ) result)
)
)
);end storm-categories
(defun storm-distribution (lst)
(setq stormcategories '(tropical-storm hurricane-cat-1 hurricane-cat-2 hurricane-cat-3 hurricane-cat-4 hurricane-cat-5))
(setq stormlist (storm-categories lst))
(let( (tropicalcount 0)
(hurricane-cat-1count 0)
(hurricane-cat-2count 0)
(hurricane-cat-3count 0)
(hurricane-cat-4count 0)
(hurricane-cat-5count 0)
(result nil)
)
(dolist (y stormlist )
(cond
((eql (second y) 'tropical-storm) (setq tropicalcount (+ tropicalcount 1)))
((eql (second y) 'hurricane-cat-1) (setq hurricane-cat-1count (+ hurricane-cat-1count 1)))
((eql (second y) 'hurricane-cat-2) (setq hurricane-cat-2count (+ hurricane-cat-2count 1)))
((eql (second y) 'hurricane-cat-3) (setq hurricane-cat-3count (+ hurricane-cat-3count 1)))
((eql (second y) 'hurricane-cat-4) (setq hurricane-cat-4count (+ hurricane-cat-4count 1)))
((eql (second y) 'hurricane-cat-5)(setq hurricane-cat-5count (+ hurricane-cat-5count 1)))
)
);ebd dolist
(push
(list (list 'tropicalstorm tropicalcount )
(list 'hurricane-cat-1 hurricane-cat-1count)
(list 'hurricane-cat-2 hurricane-cat-2count )
(list 'hurricane-cat-3 hurricane-cat-3count )
(list 'hurricane-cat-4 hurricane-cat-4count )
(list 'hurricane-cat-5 hurricane-cat-5count )
) ;end list
result) ;end push
);end let
);end distribution