We previously saw "depth-first search" -- finding an airline path between cities by recurring on its neighbors (with a base case of starting at the destination). We found that we needed to worry about cycles, and saw one way of doing so (by marking each city as it was seen).
We mentioned briefly another way of finding a path: Start at the source, then find the list of all cities which are one step away, and then all the cities which are two steps away, then three steps, etc. At each step, we can see if the destination is included. If you think about it, this ends up guaranteeing that a shortest path is found.
anecdote: word-ladder, from "white" to "house".
How to write this function? Here's a handy helper:
;; map-append: (alpha --> list-of-beta), list-of-alpha --> list-of-beta ;; Map f to each element of inputs, ;; where f returns a list each time. ;; Return the appended results. ;; (define (map-append f inputs) (foldr (lambda (frst rr) (append (f frst) rr)) empty inputs)) ; An alternate implementation, if you know "apply": ; (apply append (map f inputs))Write the function
;; one-away-from: list-of-city --> list-of-city ;; (define (one-away-from srcs) ...)Then, right a function which repeatedly calls
one-away-from
until it contains dest
.
;; keep-looking: list-of-city, city --> list-of-city ;; (define (keep-looking srcs dest) (cond [.. ..] ; Base-case [.. ..] ; inductive case: keep-looking one-away-from srcs.How does your code behave in the presence of loops? What if there is no path to the
dest
?
How would you fix this?
(The way keep-looking
indefinitely repeats
until a finish-condition,
is often called a while-loop.)
The above approach, breadth-first search is nice, except that we don't actually return the path, oops! You can think of modifying the code to return how many steps are needed, to get from a given city to another. How might we go further, and keep track of the path? (How many paths need to be kept track of simultaneously? Where should we store that information? In the depth-first approach, where was this information stored?)
set!
We saw last time, scheme passes arguments "by value":
When calling a function, the arguments (values) are copied onto a piece of paper, and handed to the function. Even if the function re-writes those pieces of paper, the caller's data isn't modified.
Using
set-first!
and
set-rest!
,
could you write this function?:
;; remove!: any, list --> (void) ;; Modify the list "items" so that ;; it no longer contains any occurrence of target. ;; (define (remove! target items) ...)What if items contained only target? (To really do this, you'd need a structure containing a list!)
Another example:
A random-number generator:
(define seed 0) ;; return a pseudo-random int in [0,100) (define (rand) (begin (set! seed (next-num seed)) seed)) ;; next-num: number --> number ;; Return the next number in an (ahem) random sequence. ;; (define (next-num n) (remainder (+ 17 (* n 41)) 100))Calling
(rand)
repeatedly yields:
This yields the sequence
17, 14, 91, 48, 85, ...
Of course, the limit 100 could be made more flexible:
How?
(define max-rand 100)
;;;;;;;;;;;;;;;;;;; (define directory empty) (define-struct entry (name number)) ;; add-entry!: symbol number --> list-of-entries ;; SIDE-EFFECT: add a new entry to direcotry. ;; We return (the updated) directory. ;; (Not necessary to return this, since anybody can ;; look up the placeholder "directory" at any time, ;; to get that same info.) ;; ;; However, a useful rule of thumb: when writing a function ;; which modifies an object but doesn't otherwise return ;; an interesting value, have it return the object being modified. ;; This is often convenient in allowing you to pass that ;; value to the next function, w/o having to use "begin". ;; (define (add-entry! name number) (begin (set! directory (cons (make-entry name number) directory)) directory)) ;; lookup: symbol --> entry or false ;; (define (lookup name) (lookup-helper name directory)) ;; lookup-helper: symbol, list-of-entries --> entry or false ;; (define (lookup-helper name entries) (cond [(empty? entries) false] [(cons? entries) (if (symbol=? (entry-name (first entries)) name) (first entries) (lookup-helper name (rest entries)))]))There is one big problem with this: the directory is public to everybody, and other programmers might (inadvertently) change the directory directly. And perhaps incorrectly, even if they're smart people: maybe your
add-entry!
prohibits duplicate entries, and your
lookup
relies on this fact,
but somebody else didn't realize this
and bypassed your
add-entry!
!
(b) An alternative use: keep track of history.
You are disgruntled, about to quit, and you write code that will
stop working after 100 times (just returning 'haha instead of answer).
Version I:
(define times-called 0) (define select-by-color (lambda (kroma) (begin (set! times-called (add1 times-called)) (if (> times-called 100) 'haha ...the real code...))))
Version II: We don't want the placeholder to be global.
Move it inside the define, before the lambda.
Question: does this make a new copy of the placeholder
every time select-by-color is called? Or just one placeholder?
(Clearly, we want the latter.)
To answer, remember the law-of-scheme, the semantics of "define".
In particular, Cf. (define x (+ 3 4)) Is + called every time x
is used, or just once? Answer: The way define works, just once.
Here, "local" is analagous to "+". It is only evaluated once,
creates a placeholder with a name inaccessible anywhere
else (say, times-called%473
),
and then it returns a value (a function which includes
times-called%473
in its closure).
What if we'd put the local inside the lambda? Then every time
we call the function, the local is evaluated, making a new placeholder
every single time, and each function has a different
times-called%xxx
in its closure.
Cf. (define function-x (lambda () (+ 3 4)))
function-x
is called, the + is evaluted.
When is an example when we might want indeed to have the local be inside the lambda, to create a local var many times? Probably when the value returned is a function and each function returned wants to use its own local variable (alternative: all functions returned refer to the same variable.) What is wrong with the following?
;; make-shopper-card: name --> (command, any --> any) ;; (define make-shopper-card (local [(define most-recently-bought #f)] (lambda (name) (lambda (command some-item) (cond [(eq? command 'show-name) name] [(eq? command 'buy) (set! most-recently-bought some-item)] [(eq? command 'show-fave) most-recently-bought])))))How to fix it?
(c) Imperative programming:
In scheme, the fundamental unit of computation is function-call.
(A functional language)
In imperative langauges (Java, C, assembly), the fundamental unit
is assign-to-variable (set!
) and sequencing (begin
),
with a lesser emphasis on calling functions.
(This functional/imperative classification is
independent of whether or not a language is object-oriented.)
set!
(These following uses are not always clean-cut; e.g. keeping track of history is one common example of keeping state.)
random
)
set!
,
you might want to reflect on which of these
purposes you're using it for.
And if not any of these,
you might want to reflect on whether
your program needs set!
,
and if the structure of the code might
better match the structure of the data
if you use one of the templates
(structure, w/ accumulator, generative).
Don't forget everything you've learned previously!
©2002 Ian Barland