Exercise 42 from the second edition of How to Design Programs explains that DrRacket highlights the last two cond clauses in the code below because the test cases do not cover all possible cases.
; TrafficLight -> TrafficLight
; given state s, determine the next state of the traffic light
(check-expect (traffic-light-next "red") "green")
(define (traffic-light-next s)
(cond
[(string=? "red" s) "green"]
[(string=? "green" s) "yellow"]
[(string=? "yellow" s) "red"]))
My understanding is that an else clause at the end should cover the remaining cases, so I tried replacing the last expressions:
(define (traffic-light-next s)
(cond
[(string=? "red" s) "green"]
[(string=? "green" s) "yellow"]
[(string=? "yellow" s) "red"]
[else "green"]))
This does not solve the highlighting problem. What is going on here?
I think you may be misunderstanding the purpose of the highlighting. The point of the code coverage tool is to make sure you have enough test cases (i.e., check-expects) to cover all the code you have written rather than ensuring that your cond clauses cover all the cases of your data definition. In your snippet, your check-expect is only testing the "red" case. You can get rid of the highlight by writing check-expects for the other two cases of your data definition.
Also note that you actually do not want to write an else case here because your data definition for a TrafficLight only contains three cases. You can't test your else case without violating your signature/contract.
Related
I'm a newbie working through HTDP2 (Felleisen et al.) on my own but have gotten stuck on question #380 of chapter IV -Intertwined Data. The problem is within the context of creating a DSL but I am first reacquainted with a general FSM Simulator and provided with the following code:
; An FSM is a [List-of 1Transition]
; A 1Transition is a list of two items:
; (cons FSM-State (cons FSM-State '()))
; An FSM-State is a String that specifies a color
; data examples
(define fsm-traffic
'(("red" "green") ("green" "yellow") ("yellow" "red")))
; FSM FSM-State -> FSM-State
; matches the keys pressed by a player with the given FSM
(define (simulate state0 transitions)
(big-bang state0 ; FSM-State
[to-draw
(lambda (current)
(overlay (text current 12 "black")
(square 100 "solid" current)))]
[on-key
(lambda (current key-event)
(find transitions current))]))
; [X Y] [List-of [List X Y]] X -> Y
; finds the matching Y for the given X in alist
(define (find alist x)
(local ((define fm (assoc x alist)))
(if (cons? fm) (second fm) (error "not found"))))
The problem is then stated as follows:
Reformulate the data definition for 1Transition so that it is possible to restrict transitions to certain keystrokes. Try to formulate the change so that find continues to work without change. What else do you need to change to get the complete program to work? Which part of the design recipe provides the answer(s)? See exercise 229 for the original exercise statement.
Exercise 229 introduces a structure type definition to keep track of the states and the transitions but since the problem statement asks me to stay within the provided find function I am hard pressed to come up with a similar structure type of definition. Instead I came up with the following Data Definition:
; An FSM is a [List-of Transition]
; A Transition is a two-item list of the following form:
; (cons FSM-State (cons (cons KeyEvent (cons FSM-State '()))'()))
; data example
(define fsm-traffic (list (list "red" (list "r" "green"))
(list "green" (list "g" "yellow"))
(list "yellow" (list "y" "red"))))
Hence calling (find fsm-traffic "green") I get (list "g" "yellow") I have thus modified the on-key clause as follows:
(lambda (current key-event)
(if (string=? key-event (first (first (rest (first transitions)))))
(second (find transitions current))
(error "invalid input")))
Now if I start the program with (simulate "red" fsm-traffic) State0 is rendered and if I press "r" it goes to "green" FSM-State but then it won't accept "g" to go to the following state and so on.
If I begin the world-program with (simulate "yellow" fsm-traffic) then FSM-State "yellow" is rendered but it will not transition to any other state (the error clause is activated); similarly with "green" as the starting state.
My hunch is that since I defined fsm-traffic with the "red" state first it accepts its input to transition to "green" but since the same is not happening with the other states big-bang is not "juggling" the transitions parameter right. But I just don't know how to fix that. I also don't know if I've gone wrong since the start with my data definition.
Thank you in advance for helping me out.
P.D. please let me know if I have followed the rules on posting on stackoverflow (this is my first post :).
You identified the source of the problem is correctly. You defined fsm-traffic with the "red" state and the "r" transition first, and the on-key handler only looks at the first. It does this in the if question:
(string=? key-event (first (first (rest (first transitions)))))
; ^ ^
; | this makes it only look at `"red"`
; this makes it only look at `"r"` within that
This if question seems complicated. Just like we would do if it was a function with a signature and purpose, we can design the inputs, output, and purpose of this expression.
Inputs: What should it depend on?
Which keys are valid depends on the transitions table, but it also depends on the current state. This is the reason why only "r" is valid on the "red" state, but "g" is valid on the "green" state. So its inputs are:
current : FSM-State
key-event : KeyEvent
transitions : FSM
Your existing expression ignores current.
Output and purpose
It is an if question, so it should output a Boolean. The purpose of this boolean is to determine whether the key is valid on the current state.
It should be true when there is any transition in the table for both the current state and the key-event.
This purpose statement with "any" and "both" sounds complicated enough that it deserves to be its own function. Using the inputs, output, and purpose we just made:
;; FSM-State KeyEvent FSM -> Boolean
;; Determines whether there is any transition in the FSM table for both the current
;; state and the key event.
(define (transition-for-state-and-key? state key table)
....)
Your current expression for the body this doesn't include the state,
(string=? key (first (first (rest (first table)))))
But the actual body should depend on both state and key.
Just like this depends on both, the find function should also depend on both state and key.
;; FSM-State KeyEvent FSM -> FSM-State
;; Finds the matching transition-state from the given state with the given key in the table.
(define (find-transition-state state key table)
....)
And note the relationship between transition-for-state-and-key? and find-transition-state. The transition-for-state-and-key? function should return true exactly when find-transition-state won't error.
This should be enough for you to continue.
If I have this function in typed/racket:
(: random-if-empty (-> (U Image-Color "empty") Image-Color))
(define (random-if-empty s)
(cond
[(equal? s "empty") (random-color)]
[else s]))
Which returns a random color if it's input is "empty", otherwise it returns it's input, how do I stop the Type Checker from saying that s (in [else s]) can be an Image-Color or "empty" instead of the expected Image-Color? Or is there a better way overall to do this? I am using the typed/2htdp/image library, which is where Image-Color comes from.
The equal? predicate can inform the type system that a variable is or is not a certain value, however, this only works for certain values of certain types. It works for some simple types (booleans, symbols, empty lists, void, and 0 and 1), but it doesn't work for most other data types, including strings.
(This might have something to do with strings being mutable, I'm not sure.)
The way to solve this is to make your own predicate for the "empty" string a different way. Typed racket provides the form make-predicate which can turn some simple "flat" types into predicates. You can use it like this:
(define my-empty-pred? (make-predicate "empty"))
This new predicate will be able to use occurrence typing more directly to tell the type system that if (my-empty-pred? x) returns true, then x has the type "empty", and if it returns false then x's type should not contain "empty". So you can use it in your example like:
(: random-if-empty (-> (U Image-Color "empty") Image-Color))
(define (random-if-empty s)
(cond
[(my-empty-pred? s) (random-color)]
[else s]))
You can take advantage of occurrence typing to tell the type checker that your s in the second case cannot be a string.
#lang typed/racket
(require typed/2htdp/image)
(define (random-color) : Image-Color
(color 0 0 0)) ;; dummy
(: random-if-empty (-> (U Image-Color "empty") Image-Color))
(define (random-if-empty s)
(cond
[(string? s) (random-color)]
[else s]))
Why does string? work and not (equal? s "empty)"? I don't know, but I guess Typed Racket isn't that smart.
You can also use assertions
(: random-if-empty (-> (U Image-Color "empty") Image-Color))
(define (random-if-empty s)
(cond
[(equal? s "empty") (random-color)]
[else (assert s string?)]))
If your types are really complicated you might have to resort to casting, which are written just like asserts. But I've given these solutions in order of preference. Casting should be a last resort.
I want to be able to do this. For example, this is my code:
(cond [true AA]
[else BB])
In AA, I want it to do 2 things. 1 is to set the value of a global variable, and then return a string. How would I go about doing that?
In the cond special form, there's an implicit begin after each condition, so it's ok to write several expressions, remembering that only the value of the last one will be returned. Like this:
(cond [<first condition>
(set! global-variable value)
"string to return"]
[else
"other return value"])
This sort of thing can be easily found in the Scheme R5RS specification. It really is worth a read as it is one of the finest language specifications ever written!
Other syntactic keywords that you might find of use:
(if <predicate> <consequent> <alternate>)
(begin <expression-or-declaration> ... <expression>)
(case <expression> <case-clause> ...)
(and <test> ...)
... others ...
create a helper function that does 2 things. have your if statement call said function when appropriate:
(cond [(test? something) (your-function your-data)]
[else (another-function your-data)])
A less straightforward way would be to call two functions and "link" them with an "and":
(cond [(test? something)
(and (do-this data) (do-that data))]
[else (do-something-else data)])
I'm learning common lisp I've been given a problem out of the uVA database (http://acm.uva.es/p/v101/10120.html) and a breadth search function (which takes in a start point, goal point and a legal move generator), i've got the theory down as to how i'm meant to get the answer but Lisp just isn't agreeing with me. Can i have some advice on how to proceed from this point onwards? Below is a link to the given problem and my two of my attempted solutions with lisp source code. Any help would be greatly appreciated! Thanks!
1.
(defun gift (N G)
(setq CR 9)
(setq i 3)
(cond ((= N G) "N and G equal")
((< N G) "Gift it on a rock outside limits")
((> N 49) "number of rocks is bigger than 49 - it will work")
((< N 9) "number of rocks is less than 9, it wont work")
((= N 0) "number of rocks is 0, it wont work")
((= G 0) "gift isn't on a rock, it wont work"))
(loop
(setq I (+ I 1))
(setq I (-(* I 2) 1))
(setq CR 9)
(breadth-search CR G #'lmg-moves)
(when (= CR G) (return "Let me Try!"))
(when (> CR N) (return "Don't laugh at me!"))
))
(defun lmg-moves (I)
(list (+ 9 I)
(- 9 I)
))
2.
(defvar *currentRock* 9)
(defvar *iterator* 3)
(defun gift (N G)
(setq *iterator* (+ *iterator* 1))
;; (breadth-search *currentRock* G #'LMG)
)
(defun LMG (a)
(+ a (-(* *iterator* 2) 1))
)
As can be seen above, the general idea is to simply apply a breadth-search function with the given legal move generator and hopefully, by analizing it's output we can determine whether we can reach the goal state or not. I will be glad to answer any questions if the code above is too confusing, thanks again!.
Among other potential issues:
You're using LOOP wrong. See PCL for info on loop. I've rehacked it a bit, but I don't know what you are attempting.
SETF is recommended over SETQ, as SETF is more general.
INCF increments a place by 1.
Your indentation is bad; if you fixed that you would notice that you're falling off the end of COND into the LOOP. I'd recommend an auto-indenting editor for using Lisp here. (Emacs is the standby).
(defun gift (N G)
(setq CR 9)
(setq i 3)
(cond ((= N G) "N and G equal")
((< N G) "Gift it on a rock outside limits")
((> N 49) "number of rocks is bigger than 49 - it will work")
((< N 9) "number of rocks is less than 9, it wont work")
((= N 0) "number of rocks is 0, it wont work")
((= G 0) "gift isn't on a rock, it wont work")) )
(loop
while t
do
(setq I (+ I 1))
(setq I (-(* I 2) 1))
(setq CR 9)
(breadth-search CR G #'lmg-moves)
(when (= CR G)
(return "Let me Try!"))
(when (> CR N)
(return "Don't laugh at me!"))))
There are some things that are immediately obvious:
You have exactly two legal return values, "Let me try!", and "Don't make fun of me!". You misspelt the first, rephrased the second, and added a lot of strings that do not have a use for the problem (are they meant as comments?).
The description calls the variables N and M, but your attempts take parameters N and G. Why confuse yourself? Either call them N and M, or (better) use meaningful names, like rock-number and gift-place.
Now, let's see your program structure.
(defun gift (N G)
(setq CR 9)
(setq i 3))
These setq instructions have undefined behaviour at this point, because CR and I are not defined yet. Many Lisp implementations will implicitly create globally special variables of these names, but it is bad style to depend on it. I have the impression that you want to use let here, like this:
(defun gift (rock-number gift-place)
(let ((current-rock 0)
(jump-number 0))
;; ...
))
Note that you should really start from the beginning, because you would miss the solution when the gift is on rock 1 or 4.
Next up, that cond form: it is dead code, because it has no side effects, and you throw away its return value immediately. It is thus at best a comment, and you should use a comment for that.
Finally, we have this funny loop:
(loop
(setq I (+ I 1))
(setq I (-(* I 2) 1))
(setq CR 9)
(breadth-search CR G #'lmg-moves)
(when (= CR G) (return "Let me Try!"))
(when (> CR N) (return "Don't laugh at me!"))))
I don't know what breadth-search does, but it seems that you really depend on the manipulation of globally special variables. I cannot say what might happen here. However, I can see several problems:
You can have up to two locations when jumping a certain distance from a given rock. It cannot be right to check only a single variable after each jump.
You seem to confuse the jump number with its jump distance. I goes in the sequence 1, 3, 7, 15 …, but the jump number sequence would be 1, 2, 3, 4 … while the jump distance sequence would be 1, 3, 5, 7 …. Even the rocks visited when always jumping right are a different sequence (1, 4, 9, 16 …).
You reset CR to 9 each time through the loop. I do not see how that could be right.
Stylistically, you should keep your variables as local as possible, using for example let, do, or the extended loop keywords :for and :with, then pass them into the functions that need them as arguments. This makes it much easier to reason about what is happening.
I think that your mental model of the solution algorithm is a bit confused. I would structure this in such a way that you loop over the jumps and keep a set of rocks that you can possibly be on after exactly this number of jumps. A special treatment for small N does not seem to really give a lot of efficiency gain. If you have a proof that N > 49 always has a solution, on the other hand, you should have a guard clause and a comment that outlines the proof.
I'm working with clojure and while I've dabbled with lisps before, I'm having trouble finding a clean way to nest let statements in cond statements. For example, consider the following function:
(defn operate-on-list [xs]
(let [[unpack vector] (first xs)]
(cond
[(empty? xs) 'empty
unpack vector
:else (operate-on-list (rest xs))])))
It's a pretty standard recursive operation on a list, but it needs to do some work on the first element in the list before it works with the contents. The issue, of course, is that the list may be empty.
In this example, it wouldn't be hard to change unpack to ((first xs) 0) and vector to ((first xs) 1), but this quickly gets ugly if more work needs to be done on (first xs).
Is there any way to effectively use a let statement part-way through a cond?
Thanks.
-Nate
In cases like these, you're best off using if-let:
(defn operate-on-list [xs]
(if-let [[unpack v] (first xs)]
(cond
unpack v
:else (operate-on-list (rest xs)))))
This code walks the given list seq-able (list, vector, array...) of vectors and returns the second element of the first vector whose first element is true (meaning not false or nil). nil is returned if no such vector is found.
Note that vector is a built-in function, so I've chosen v as the variable name, just in case the need to use the function in the body arises in the future. More importantly, you're using too many brackets in your cond syntax; fixed in this version.
UPDATE: Two additional things worth noting about if-let:
The way if-let works, if (first xs) happens to be nil (false would be the same), the destructuring binding never takes place, so Clojure won't complain about not being able to bind nil to [unpack v].
Also, if-let accepts an else clause (in which you can't refer to the variables bound in if-let bindings vector -- though if you're in the else clause, you know they where false or nil anyway).
;use conditional let: http://richhickey.github.com/clojure-contrib/cond-api.html
(use 'clojure.contrib.cond)
(cond-let [b]
nil b
12 (prn (+ b 1))
:else 17 )
;==> 13
Another good example can be found here http://www.mail-archive.com/clojure#googlegroups.com/msg03684.html
Sort of like this, with a let inside the scope of the cond?
(defn operate-on-list [list]
(let [ el_first (first list) ]
(cond
(nil? el_first) (println "Finished")
:else (do
(let [ list_rest (rest list) ]
(println el_first)
(operate-on-list list_rest))))))
(operate-on-list '(1 2 3))
The output is:
1
2
3
Finished