(define-struct animal (name species age breakfasthour dinnerhour))
(define-struct attendant (name a1 a2 a3))
(define gorilla (make-animal "Koko" "Gorilla" 4 "8" "10"))
(define bat (make-animal "Bruce" "Bat" 1 "23" "5"))
(define mandrill (make-animal "Manny" "Mandrill" 5 "8" "7"))
(define crocodile (make-animal "Swampy" "Crocodile" 1 "10" "8"))
(define ocelot (make-animal "Ozzy" "Ocelot" 7 "7" "17"))
(define capybara (make-animal "Capy" "Capybara" 4 "6" "8"))
(define potto (make-animal "Spot" "Potto" 2 "2" "6"))
(define tapir (make-animal "Stripey" "Tapir" 3 "10" "6"))
(define vulture (make-animal "Beaky" "Vulture" 10 "9" "6"))
(define attendant1 (make-attendant "Dave" gorilla bat mandrill))
(define attendant2 (make-attendant "John" crocodile ocelot capybara))
(define attendant3 (make-attendant "Joe" potto tapir vulture))
I need a function that takes an animal and returns whether its mealtime, if i take gorilla, then dinner time would be at 10. This is what I've done. ignore the quotes on the numbers above.
(define (meal-time? e1 e2)
(string=? (animal-species e1)
(animal-dinnerhour e2)))
it runs, but wnt give me an output. any reason why it wont give me an output?
edit- (meal-time? gorilla 10)tells me it expects an animal, but given 10.
Your meal-time? function takes two animals as arguments (because you use animal- accessor functions on both arguments), but you call it with an animal and a number. So you get an error message telling you that the second argument should be an animal.
If you call your function with two animals as arguments, you'll get no error any more. You'll get #f. What your function does is: it checks whether the species of the first animal is equal to the the dinner hour of the second animal. Since there is no species whose name is a number, that will never be true.
Related
I am learning Scheme and I keep getting this error: "Error: 20 is not a function" from the following code:
(define myFunction (lambda (x y)
(* x y)))
(define (higherOrder func x y)
(
func x y))
(display ((higherOrder myFunction 4 5)))
I am trying to pass a function as one of the arguments. It goes through with the math since it says "20" in the error message and (5 * 4 = 20) but then it thinks it is a function. What is the problem? I cannot figure it out. I am running this code on https://repl.it/languages/Scheme.
You have one too many pairs of parens, the expression (higherOrder myFunction 4 5) evaluates to the integer 20, then the repl tries to evaluate (20), which it can't because 20 isn't a function. When Scheme evaluates a list (where a list is anything in parens that isn't quoted) the first entry in the list is assumed to be a function.
Change the last line to
(display (higherOrder myFunction 4 5))
I'm trying to create a function that will take a string and display it.
(defun closing (s)
(format t "~{~a~}" ("Sincerely," "\n" s)))
What I hope to get is
Sincerely,
Frank
if "Frank" is the string I passed in. It complains of the variable S is defined but never used. What am I doing wrong?
Trying to use format alone: If I declare urname as a defparameter to be "Frank", the following doesn't print Frank, rather just the variable name. (Without quote it complains of urname not being a function.)
(format t "~{~a~}" '(urname urname urname))
How can I feed variables to format?
There are three issues here: (1) The code you posted doesn't just have the problem of not using s; it's also trying to call the string "Sincerely" as a function; (2) quoting a list means you'll get exactly what's quoted (e.g., a list of symbols, not a list of values of variables); (3) calling format with lists.
(something other-stuff...) is a function call
When I put the code you posted into SBCL, I get some very specific and helpful output:
CL-USER> (defun closing (s)
(format t "~{~a~}" ("Sincerely," "\n" s)))
; in: DEFUN CLOSING
; ("Sincerely," "n" S)
;
; caught ERROR:
; illegal function call
; (SB-INT:NAMED-LAMBDA CLOSING
; (S)
; (BLOCK CLOSING (FORMAT T "~{~a~}" ("Sincerely," "n" S))))
;
; caught STYLE-WARNING:
; The variable S is defined but never used.
;
; compilation unit finished
; caught 1 ERROR condition
; caught 1 STYLE-WARNING condition
("Sincerely," "\n" s) is an illegal function call, since a string, like "Sincerely", can't have a function binding. Since SBCL sees the problem in that, it recognizes that the one thing that s might have been used for (i.e., an argument to a function call), can't happen. That's why you'll get the error, and then the associated style warning.
Creating lists of values
The second is probably answered in other questions already, but the short answer is that you want (list x y z), not '(x y z). The former calls the function list with the values of the variables x, y, and z, while the latter denotes a literal list of the symbols x, y, and z.
CL-USER> (let ((a 42)
(b 89))
(print '(a b)) ; a list of two symbols
(print (list a b))) ; a list of two numbers
(A B)
(42 89)
Format, iteration, &c.
The third is probably more interesting, since format has so many possibilities. The ~{ and ~} in your example are used for iterating over values in a list. First, let's look at a simple example: you can just use the format directive ~a and call format with the arguments you want to splice in:
CL-USER> (let ((closing "Sincerely")
(name "Frank"))
(format t "~a,~%~a" closing name))
Sincerely,
Frank
Now, if you need to print multiple values, you can use ~{ and ~} to have format iterate over a list of values:
CL-USER> (let ((closing "Sincerely")
(names '("Frank" "John")))
(format t "~a,~{~%~a~}" closing names))
Sincerely,
Frank
John
If the names are the values of variables, then you can either create a list containing those values:
CL-USER> (let ((closing "Sincerely")
(name1 "Frank")
(name2 "John"))
(format t "~a,~{~%~a~}" closing (list name1 name2)))
Sincerely,
Frank
John
or you can change ~{ to ~#{ and have format read the remaining arguments as the list:
CL-USER> (let ((closing "Sincerely")
(name1 "Frank")
(name2 "John"))
(format t "~a,~#{~%~a~}" closing name1 name2))
Sincerely,
Frank
John
You should read a tutorial about format, from here for example
For an easy explanation
(format
(destination-stream usually t for standard output nil to form a string)
(here comes the string)
(&rest variables that should write in the string where is an ~A and by position like String.format in java or c))
in your case, you need the symbol ~% or use the character for return in common lisp
CL-USER> (defun closing (s) (format t "~A~%~%~A" "Sincerely," s))
CLOSING
CL-USER> (closing "paco")
Sincerely,
paco
NIL
The nil say that the functions returns null, and the other is the standard output if you want to return a string, put nil instead of t
In my program the user will enter 3 elements for each student:
ID
Name
Grade
These are put into a list which will look like (a scheme list)
( (001 "Bob" 80) (002 "Sam" 85) (003 "Aaron" 94) etc . . .)
If the user chooses to sort by name, I'd then like the list to display the info like:
No.1: ID=003, Name=’’Aaron’’, Grade=94
No.2: ID=001, Name=’’Bob’’, Grade=80
No.3: ID=002, Name=’’Sam’’, Grade=85
I finished the function to generate the list but i'm struggling with sorting the list and displaying. any help would be appreciated, thanks
An implementation of sort in Scheme is found in Wikibooks. It is called mergesort. At its core, it assumes that you can use < to compare two elements of the list for sorting purposes.
You can modify mergesort to take an additional argument, less-proc, and use it wherever < is used.
Then you can call mergesort with:
(mergesort lst (lambda (a b) (string<? (cadr a) (cadr b))))
Check your interpreter's documentation for a sorting procedure. For example in Racket you can sort the following list:
(define lst '((001 "Bob" 80) (002 "Sam" 85) (003 "Aaron" 94)))
Ascending, using the name:
(sort lst #:key second string<?)
=> '((3 "Aaron" 94) (1 "Bob" 80) (2 "Sam" 85))
Descending, using the grade:
(sort lst #:key third >)
=> '((3 "Aaron" 94) (2 "Sam" 85) (1 "Bob" 80))
… You get the idea. For the second part of the question, once again refer to your interpreter's documentation. In Racket printf comes in handy - for instance, for printing the records after sorting them by name:
(for ([i (in-naturals 1)]
[record (sort lst #:key second string<?)])
(printf "No.~a: ID=~a, Name=’’~a’’, Grade=~a~n"
i
(~a (first record) #:min-width 3 #:align 'right #:left-pad-string "0")
(second record)
(third record)))
=> No.1: ID=003, Name=’’Aaron’’, Grade=94
No.2: ID=001, Name=’’Bob’’, Grade=80
No.3: ID=002, Name=’’Sam’’, Grade=85
I've been working on this today and what I am left to do is develop a function that takes an attendant and returns the total age of the animals the attendant has been assigned to. So if I take for example attendant Dave, I would get 10. Not sure where to start. How would you add the ages up?
(define-struct animal (name species age breakfasthour dinnerhour))
(define-struct attendant (name a1 a2 a3))
(define gorilla (make-animal "Koko" "Gorilla" "4" "8" "10"))
(define bat (make-animal "Bruce" "Bat" "1" "23" "5"))
(define mandrill (make-animal "Manny" "Mandrill" "5" "8" "7"))
(define crocodile (make-animal "Swampy" "Crocodile" "1" "10" "8"))
(define ocelot (make-animal "Ozzy" "Ocelot" "7" "7" "17"))
(define capybara (make-animal "Capy" "Capybara" "4" "6" "8"))
(define potto (make-animal "Spot" "Potto" "2" "2" "6"))
(define tapir (make-animal "Stripey" "Tapir" "3" "10" "6"))
(define vulture (make-animal "Beaky" "Vulture" "10" "9" "6"))
(define attendant1 (make-attendant "Dave" gorilla bat mandrill))
(define attendant2 (make-attendant "John" crocodile ocelot capybara))
(define attendant3 (make-attendant "Joe" potto tapir vulture))
#;(define (meal-time? e1 e2)
(string=? (animal-species e1)
(animal-dinnerhour e2)))
#;(define (animal-template s...)
(...(animal-name s)...
(animal-species s)...
(animal-age s)...
(animal-breakfasthour s)...
(animal-dinnerhour s)...))
#;(define (attendant-template s...)
(...(attendant-name s)...
(attendant-s1 s)...
(attendant-s2 s)...
(attendant-s3 s)...))
Simply use the accessor procedures for each type:
(define (animals-age att)
(+ (animal-age (attendant-a1 att))
(animal-age (attendant-a2 att))
(animal-age (attendant-a3 att))))
Clearly, this works only if the age is a number, in your current code is a string (why?), please consider representing ages as numbers, it makes more sense.
This question and another question of mine sort of merged into one after I figured a few things out, so I revised this question.
What I'm trying to accomplish with my function is outlined below.
Iterate over all of the spots. If it's open, select the spot with the current player's symbol.
If this move causes the game to be won and it's the computer player's turn, add a key-value pair of the spot (an integer) and the score of the spot (an integer, 1 in this case) to the scored-spots hash-map.
Recurse and call this same function, passing it the new scored-spots hash-map, the board with the move just made removed, the same player, and the same symbol.
If however, the game has not been won, proceed to the next conditional statement and check that.
Proceed with the next conditional statements in the same way, just with different scores (a win with the computer's turn is 1, a win with the human's turn is -1, a tie is 0).
If none of the conditional statements evaluate to true, recurse anyway (the scored-spots hash-map won't be any different in this case).
Here's the code I tried, but this isn't returning the values I'm expecting.
Note:
board is a hash-map like this: {0 "0", 1 "1", 2 "2"} (spot location - spot value)
sym is a symbol, like "X" or "O"
current-player is a keyword, like :computer or :human
scored-spots is a hash-map like this: {}
(defn score-spots [board sym current-player scored-spots]
(for [spot (keys board)]
(if (some #(= % (.toString spot)) (filter #(not= "O" %) (filter #(not= "X" %) (vals board))))
(let [board (assoc board spot sym)]
(cond
(and (game-won board) (= current-player :computer))
(score-spots board sym current-player (assoc scored-spots spot 1))
(and (game-won board) (= current-player :human))
(score-spots board sym current-player (assoc scored-spots spot -1))
(game-tied board)
(score-spots board (switch-symbol sym) (switch-player current-player) (assoc scored-spots spot 0))
:else
(score-spots board (switch-symbol sym) (switch-player current-player) scored-spots)))
scored-spots))))
What I'm expecting as a return value is a hash-map with each open spot scored. For example, {1 0, 4 1, 5 -1, 6 -1, 8 0}.
Instead, if I pass it this board:
{1 "X" 2 "X" 3 "O" 4 "4" 5 "5" 6 "6" 7 "7" 8 "X" 9 "O"},
I get a return value with a large list of hash-maps.
I'm relatively new to Clojure and FP in general. Whenever I think about recursion, I always try to first think if it is an opportunity for map and/or reduce.
In this case, you're trying to score each spot. Each spot collected together is a board. So if I can score each spot and then collect them together, I can accomplish the task at hand. In reduce terms, I can do something to an item (the spot) in a collection, and then merge that value into a single value (the board - technically only the spots without "X" or "O" for the code below).
Here's my rewrite:
(defn score-spot [scored-spot current-player board]
(let [[spot score] scored-spot]
(cond
(and (game-won board) (= current-player :computer)) {spot 1}
(and (game-won board) (= current-player :human)) {spot -1}
(game-tied board) {spot 0}
:else {spot score})))
(defn score-board [board current-player]
(let [spots-to-score (filter #(and (not= "X" (second %))
(not= "O" (second %))) board)]
(reduce #(into %1 (score-spot %2 current-player board)) {} spots-to-score)))
This would give your result of e.g. {1 0, 4 1, 5 -1, 6 -1, 8 0}
Edit:
Regarding the need to recur, you're basically wanting to use mutual recursion. For that, you can use declare to forward declare functions and then use trampoline (here's a quick tutorial) for the actual recursion.