Question asked:
Define a LISP function SCORE->GRADE which takes a single argument, s, and returns a symbol according to the following scheme:
s ≥ 90 A 73 ≤ s < 77 C+
87 ≤ s < 90 A– 70 ≤ s < 73 C
83 ≤ s < 87 B+ 60 ≤ s < 70 D
80 ≤ s < 83 B s < 60 F
77 ≤ s < 80 B–
If the argument s is not a number then the function should return NIL.
My answer is this:
(defun SCORE->GRADE (s)
(if (not (numberp s)) (return-from SCORE->GRADE “NIL”))
(progn
(if (>= s 90) (return-from SCORE->GRADE "A"))
(if (and (>= s 87) (< s 90)) (format nil “A-“))
(if (and (>= s 83) (< s 87)) (format nil “B+”))
(if (and (>= s 80) (< s 83)) (return-from SCORE->GRADE “B”))
(if (and (>= s 77) (< s 80)) (return-from SCORE->GRADE “B-“))
(if (and (>= s 73) (< s 77)) (return-from SCORE->GRADE “C+”))
(if (and (>= s 70) (< s 73)) (return-from SCORE->GRADE “C”))
(if (and (>= s 60) (< s 70)) (return-from SCORE->GRADE “D”)
(if (< s 60) (return-from SCORE->GRADE “F”))
)
)
)
It works for 90, returns A, then for anything else it just gives this error, with different variables in regards to what I enter
*** - RETURN-FROM: variable “B” has no value
*** - IF: variable “A-“ has no value
Can anyone explain why I can't get the same result for every line that is incredibly identical?
I've tried message, format t, cases, some work for up to the first 3 cases then stop. Haven't been able to figure anything out.
In addition to other answers, note that in your case you do not need to duplicate common boundaries, since you are trying to partition scores into grades.
(cond
((>= s 90) "A")
((>= s 87) "A-")
((>= s 83) "B+")
...
((>= s 70) "C")
((>= s 60) "D")
(t "F"))
It also reduces the number of invariants you have to keep in your code, which helps with maintenance.
when you fixed your problem with the double quotes, here are some additional hints:
you usually don't need to use RETURN-FROM in Lisp.
checking multiple conditions is best done with COND and not by multiple IF statements.
functions like >=, >, ... can take more than two arguments.
see for example:
(cond ((not (numberp s)) NIL)
((>= s 90) "A" )
((>= 90 s 87) "A-")
((>= 87 s 83) "B+")
((>= 83 s 80) "B" )
;...
((>= 60 s ) "F" ))
It seems that you are using the character “ instead of " to wrap the strings.
I had the same idea as #coredump.
(defun SCORE->GRADE (s)
(cond ((not (numberp s)) nil)
((>= s 90) "A")
((>= s 87) "A-")
((>= s 83) "B+")
((>= s 80) "B")
((>= s 77) "B-")
((>= s 73) "C+")
((>= s 70) "C")
((>= s 60) "D")
(t "F")))
Which again can be slightly more elegantly formulated as:
(defun SCORE->GRADE (s)
(when (numberp s)
(cond ((>= s 90) "A")
((>= s 87) "A-")
((>= s 83) "B+")
((>= s 80) "B")
((>= s 77) "B-")
((>= s 73) "C+")
((>= s 70) "C")
((>= s 60) "D")
(t "F"))))
Related
I am trying to understand Racket's macros. For that, I built this function for a specific purpose:
(define (while-less-fun max)
(cond [(< a (- max 1))
(and (begin (set! a (+ a 1)) (print "x") a) (while-less-fun max))]
[(< a max)
(begin (set! a (+ a 1)) (print "x") a)]
[(>= a max)
(begin (set! a (+ a 1)) (print "x") a)]
))
It works as intended on the REPL:
> a
2
> (while-less-fun 7)
"x""x""x""x""x"7
> a
7
I tried to transform the function above in the macro bellow. My goal would be to generate the same results:
(define-syntax while-less
(syntax-rules (do)
[(while-less maximo do begin-exp)
(cond [(< a (- maximo 1))
(and begin-exp (while-less maximo do begin-exp))]
[(< a maximo)
begin-exp]
[(>= a maximo)
begin-exp])]))
The expected result was:
(define a 2)
(while-less 7 do (begin (set! a (+ a 1)) (print "x") a))
(while-less 7 do (begin (set! a (+ a 1)) (print "x") a))
Evaluating the second line would print "x" 5 times and change a to be 7. Also, evaluating the third line would print "x" 1 time and would change a to be 8.
Unfortunately, this generates an infinite recursion which really surprises me.
What did I do wrong? Why did this infinite recursion happen?
I know that you should avoid writing macros when a function is able to solve the problem. But, is my approach the wrong way of trying to accomplish this task?
In the first cond clause, you call "(while-less maximo do begin-exp)" with the same arguments, the entire body of the macro will be expanded again by calling the same "(while-less maximo do begin-exp)", causing the loop.
What you can do is create a generic while for a condition:
(define-syntax while
(syntax-rules (do)
[(while condition do begin-exp)
(let loop ()
(when condition
begin-exp
(loop)))]))
> (define a 2)
> (while (< a 7) do (begin (set! a (+ a 1)) (print "x") a))
"x""x""x""x""x"
> (while (< a 7) do (begin (set! a (+ a 1)) (print "x") a))
>
This program is forbidden to use any kinds of list. I tried the following method but it is crazy.
(define (order2 number2)
(cond ((remainder number2 10) 1)
(else (order3 number3))))
(define (order3 number3)
(cond ((< number3 10) number3)
(else
(define (order4 number4)
(cond (< remainder (quotient number4 10)))
(else (order4 (quotient number4)))))))
))
So i have this macro (basically a for loop):
(defmacro for ((parameter start-value end-value &optional (step 1)) &body e)
(let ((func-name (gensym))
(end (gensym)))
`(labels ((,func-name (,parameter ,end)
(if (<= ,parameter ,end)
(progn ,#e
(,func-name (+ ,parameter ,step) ,end)))))
(,func-name ,start-value ,end-value))))
And i want to test it with this:
(print (let ((j 0) (k 1))
(for (i 1 10 (incf k)) (print i))))
What i get now is:
1, 3, 6, 10, NIL.
which means that my step increments after each iteration, but i want it to increment only once in the beginning for this output:
1, 3, 5, 7, 9, NIL.
What's wrong with my macro and what should i do?
You need to compute the step value outside the loop:
CL-USER 12 > (defmacro for ((parameter start-value end-value
&optional (step 1))
&body e)
(let ((func-name (gensym))
(step-name (gensym))
(end (gensym)))
`(labels ((,func-name (,parameter ,end ,step-name)
(when (<= ,parameter ,end)
,#e
(,func-name (+ ,parameter ,step-name)
,end
,step-name))))
(,func-name ,start-value ,end-value ,step))))
FOR
CL-USER 13 > (pprint (macroexpand-1 '(for (i 1 10 (incf k)) (print i))))
(LABELS ((#:G954 (I #:G956 #:G955)
(WHEN (<= I #:G956) (PRINT I) (#:G954 (+ I #:G955) #:G956 #:G955))))
(#:G954 1 10 (INCF K)))
CL-USER 14 > (let ((j 0) (k 1))
(for (i 1 10 (incf k))
(print i)))
1
3
5
7
9
NIL
If you don't want to pass the step-value all the time, you need an outer LET binding its value.
Note: some Lisp implementations (many interpreters and some compilers) don't support TCO (tail call optimisation).
I have this function:
(defun test (variable)
(cond
((null variable) nil)
((< (- 12 (other-function variable) 3) 0) 1)
(t (- 12 (other-function variable) 3))
)
)
The idea is, if the result of the subtraction of 12 with the value of the function with 3 is less than 0, it returns 1. Else, it will just make the subtraction.
The "other-function" returns a number.
When i run this function, lispworks freezes. But if i run the function without the first condition:
(defun test (variable)
(cond
((null variable) nil)
(t (- 12 (other-function variable) 3))
)
)
it makes the subtraction without any problem.
Can someone help?
Thanks.
EDIT:
I tried this way with let:
(defun test (variable)
(let (x (other-function variable))
(cond
((null variable) nil)
((< (- 12 x 3) 0) 1)
(t (- 12 x 3)))
)
)
But i still got the same problem with lispworks wich is, it freezes. When i run without the following condition:
((< (- 12 x 3) 0) 1)
This function works correctly.
Unless you come up with your full code and a test case, this can't be reproduced.
CL-USER 1 > (lisp-implementation-type)
"LispWorks"
CL-USER 2 > (defun test (variable)
(cond
((null variable) nil)
((< (- 12 (other-function variable) 3) 0) 1)
(t (- 12 (other-function variable) 3))))
TEST
CL-USER 3 > (defun other-function (foo) (+ foo 1))
OTHER-FUNCTION
CL-USER 4 > (test 5)
3
CL-USER 5 > (test 500)
1
Also: LispWorks usually does not 'freeze' on an error.
I am trying to write a Scheme function that takes in a list of letters, hashes them with another function, multiplies each one iteratively by 5^i, and sums them together. I am new to Scheme and this is what I have written:
(define (key w)
keyhelper w 0)
(define (keyhelper w i)
(cond ((null? w) '())
(else (+ (* (hashchar(car w) (expt 5 i)) (keyhelper(cdr w) (+ i 1)))))))
So for example, doing (key '(h e l l o)) should do hashchar(h)*5^0 + hashchar(e)*5^1 + hashchar(l)^5^2 ... etc. The function is only returning 0 for any list that is sent in. Could anyone please tell me where I am going wrong?
My implementation of hashchar is:
(define hashchar
(lambda (x)
(cond
((eq? x 'a) 1)
((eq? x 'b) 2)
((eq? x 'c) 3)
((eq? x 'd) 4)
((eq? x 'e) 5)
((eq? x 'f) 6)
((eq? x 'g) 7)
((eq? x 'h) 8)
((eq? x 'i) 9)
((eq? x 'j) 10)
((eq? x 'k) 11)
((eq? x 'l) 12)
((eq? x 'm) 13)
((eq? x 'n) 14)
((eq? x 'o) 15)
((eq? x 'p) 16)
((eq? x 'q) 17)
((eq? x 'r) 18)
((eq? x 's) 19)
((eq? x 't) 20)
((eq? x 'u) 21)
((eq? x 'v) 22)
((eq? x 'w) 23)
((eq? x 'x) 24)
((eq? x 'y) 25)
((eq? x 'z) 26))))
key returns zero all the time because you defined it that way. You had:
(define (key w)
keyhelper
w
0)
thus, it evaluates keyhelper (discarding its value), then w (discarding its value), then 0 (returning its value). So the answer is always 0.
You should instead define it this way:
(define (key w)
(keyhelper w 0))
Notice the extra parentheses.
Also, the base-case value for keyhelper is wrong. It shouldn't be '(), it should be i.
If your definition of hashchar is similar to this one:
(define (hash:hash-char-ci char n)
(modulo (char->integer (char-downcase char)) n))
(define hash:hash-char hash:hash-char-ci)
Then hashchar will return 0 when i = 0 is passed to (expt 5 i) because (expt 5 i) is one, and the one-modulo of any integer is zero.
Once you multiply zero into your hash function, then you'll always get zero out...since + isn't doing anything but returning identity because it is only passed one argument:
(* (hashchar(car w) (expt 5 i)) (keyhelper(cdr w) (+ i 1)))
Maybe string-hash is a better choice of library function?