This is from the MIT 6.001 Online Tutor, it's part of the third problem set.
Question: Indicate the type of each of the following expressions. If you need type variables, use A,B,C, etc., starting with A as the leftmost variable.
(lambda (x y) x) = A,B->A
(lambda (p) (p 3))
(lambda (p x) (p x)) = (A->B), A->B
(lambda (x y comp) (if (comp x y) x y))
As you can see I solved 1 and 3, but that was mainly out of luck. I still am having issues with understanding the concept and that is stopping me from solving 2 and 4.
Lecture slides can be found here (view the last few).
A, B -> A
(number -> A) -> A
(A -> B), A -> B
A, A, (A, A -> boolean) -> A
(the last assumes that x and y are the same types)
Related
I'm having trouble figuring out how to sort out the 2 biggest numbers and return them into the sum of squares procedure. I am struggling to write the code out in Scheme's syntax. I'm trying to write it as cleanly as possible, and I keep running circles in my head and on paper trying to do so. the book describes thinking "procedurally" and I think I'm having trouble with that aspect.
The book provides code for the sum-of-squares and square procedures. I would include my pseudo code but I'm severely lost. Here is the code the book provides:
(define (square x) (* x x))
(define (sum-of-squares x y)
(+ (square x) (square y)))
How to define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers?
How to define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers?
First you need a name for the procedure. Let's called it sum-of-squares-two-largest.
(define (sum-of-squares-two-largest x y z)
...)
It can make use of the sum-of-squares function, but it needs to find the two largest numbers out of x,y,z first.
One way to do this would be to get rid of the smallest number. You could define a helper procedure smallest? a b c that checks that a is the smallest of the 3 numbers by doing (and (<= a b) (<= a c)).
(define (sum-of-squares-two-largest x y z)
(if (smallest? x y z)
(sum-of-squares y z)
(if (smallest? y x z)
...]
Write the code for min-of-three. Its negative (as in photography) is what you need:
(define (negative-min-of-three a b c)
(if (<= a b)
(if (<= a c)
(..... b ... c .....)
(..... a ... b .....))
(if (<=
..........
You can complete the code, and rename it. The clock is ticking!
I created two methods to get the largest and the one in the middle for the time being.
(define (largest x y z)
(cond ((and (> x y) (> x z)) x)
((and (> y x) (> y z)) y)
(else z))
)
(define (mid x y z)
(cond ((and (> x y) (< x z)) x)
((and (> y x) (< y z)) y)
(else z))
)
(define (square a)
(* a a)
)
(define (sum-of-two-largest x y z)
(+ (square (largest x y z)) (square (mid x y z)))
)
(sum-of-two-largest -12 -4 1)
The hard part of this is, if you're reading SICP book, finding the 2nd largest number of the three. You can observe, if we let c < b < a, that:
a = max(a, b) gives the largest of the two numbers
b = max(c, b) gives the largest of the two smaller numbers
But how do we get the variable b on the second line. It can so happen that a is the smaller of the two? We can observe that:
b = min(a, b)
If we substitute min(a, b) for b in the max function on the third line, we get:
b = max(c, min(a, b))
This strategy is implemented in the following code, using only the constructs introduced in the book so far:
(define (square x) (* x x))
(define (max a b) (if (> a b) a b))
(define (min a b) (if (< a b) a b))
(define (sum-of-squares-two-largest a b c)
(+ (square (max a b)) (square (max c (min a b)))))
(sum-of-squares-two-largest 1 2 3)
(sum-of-squares-two-largest 1 2 1)
(Details of my miniKanren in Racket setup appear at the bottom[1].)
The way quotes and unquotes work in The Reasoned Schemer appears not to match the way they work in Racket. For instance, verse 2 of chapter 2 suggests[2] the following function definition:
(run #f
(r )
(fresh (y x )
(== '(,x ,y) r )))
If I evaluate that, I get '((,x ,y)). If instead I rewrite it as this:
(run #f
(r )
(fresh (y x )
(== (list x y) r)))
I get the expected result, '((_.0 _.1)).
This might seem like a minor problem, but in many cases the required translation is extremely verbose. For instance, in exercise 45 of chapter 3 (page 34), the book provides, roughly[3] the following definition:
(run 5 (r)
(fresh (w x y z)
(loto (('g 'g) ('e w) (x y) . z))
(== (w (x y) z) r)))
In order to get the results they get, I had to rewrite it like this:
(run 5 (r)
(fresh (w x y z)
(loto (cons '(g g)
(cons (list 'e w)
(cons (list x y)
z))))
(== (list w (list x y) z)
r)))
[1] As described here, I ran raco pkg install minikanren and then defined a few missing pieces.
[2] Actually, they don't write precisely that, but if you heed the advice in the footnotes to that verse and an earlier verse, it's what you get.
[3] Modulo some implicit quoting and unquoting that I cannot deduce.
Use the backquote ` instead of the simple quote ' you have been using.
I was just beginning to feel I had a vague understanding of the use of lambda in racket and scheme when I came across the following 'alternate' definitions for cons and car in SICP
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
For the life of me I just cannot parse them.
Can anybody explain how to parse or expand these in a way that makes sense for total neophytes?
This is an interesting way to represent data: as functions. Notice that this
definition of cons returns a lambda which closes over the parameters x
and y, capturing their values inside. Also notice that the returned lambda
receives a function m as a parameter:
;creates a closure that "remembers' 2 values
(define (cons x y) (lambda (m) (m x y)))
;recieves a cons holding 2 values, returning the 0th value
(define (car z) (z (lambda (p q) p)))
;recieves a cons holding 2 values, returning the 1st value
(define (cdr z) (z (lambda (p q) q)))
In the above code z is a closure, the same that was created by cons, and in
the body of the procedure we're passing it another lambda as parameter,
remember m? it's just that! the function that it was expecting.
Understanding the above, it's easy to see how car and cdr work; let's
dissect how car, cdr is evaluated by the interpreter one step at a time:
; lets say we started with a closure `cons`, passed in to `car`
(car (cons 1 2))
; the definition of `cons` is substituted in to `(cons 1 2)` resulting in:
(car (lambda (m) (m 1 2)))
; substitute `car` with its definition
((lambda (m) (m 1 2)) (lambda (p q) p))
; replace `m` with the passed parameter
((lambda (p q) p) 1 2)
; bind 1 to `p` and 2 to `q`, return p
1
To summarize: cons creates a closure that "remembers' two values, car
receives that closure and passes it along a function that acts as a selector for
the zeroth value, and cdr acts as a selector for the 1st value. The key
point to understand here is that lambda acts as a
closure.
How cool is this? we only need functions to store and retrieve arbitrary data!
Nested Compositions of car & cdr are defined up to 4 deep in most LISPs. example:
(define caddr (lambda (x) (car (cdr (cdr x)))))
In my view, the definitive trick is reading the definitions from the end to the beginning, because in all three of them the free variables are always those that can be found in the lambda within the body (m, p and q). Here is an attempt to translate the code to English, from the end (bottom-right) to the beginning (top-left):
(define (cons x y)
(lambda (m) (m x y))
Whatever m is, and we suspect it is a function because it appears right next to a (, it must be applied over both x and y: this is the definition of consing x and y.
(define (car z)
(z (lambda (p q) q)))
Whatever p and q are, when something called z is applied, and z is something that accepts functions as its input, then the first one of p and q is selected: this is the definition of car.
For an example of "something that accepts functions as its input", we just need to look back to the definition of cons. So, this means car accepts cons as its input.
(car (cons 1 2)) ; looks indeed familiar and reassuring
(car (cons 1 (cons 2 '()))) ; is equivalent
(car '(1 2)) ; is also equivalent
(car z)
; if the previous two are equivalent, then z := '(1 2)
The last line means: a list is "something that accepts a function as its input".
Don't let your head spin at that moment! The list will only accept functions that can work on list elements, anyway. And this is the case precisely because we have re-defined cons the way that we have.
I think the main point from this exercise is "computation is bringing operations and data together, and it doesn't matter in which order you bring them together".
This should be easy to understand with the combinatory notation (implicitly translated to Scheme as currying functions, f x y = z ==> (define f (λ (x) (λ (y) z)))):
cons x y m = m x y
car z = z _K ; _K p q = p
cdr z = z (_K _I) ; _I x = x _K _I p q = _I q = q
so we get
car (cons x y) = cons x y _K = _K x y = x
cdr (cons x y) = cons x y (_K _I) = _K _I x y = _I y = y
so the definitions do what we expect. Easy.
In English, the cons x y value is a function that says "if you'll give me a function of two arguments I'll call it with the two arguments I hold. Let it decide what to do with them, then!".
In other words, it expects a "continuation" function, and calls it with the two arguments used in its (the "pair") creation.
On Exercise (or entry?) 57, I'm just not understanding how the logic flows. The question is that this: given
(define teacupo
(lambda (x)
(conde
((= tea x ) #s)
((= cup x ) #s)
(else #u))))
where '=' is actually the triple-bar unify (?) operator. Running the following:
(run* (r)
(fresh (x y)
(conde
((teacupo x) (= #t y) #s)
((= #f x) (= #t y))
(else #u)
(= (cons x (cons y ())) r)))
the book gives the answer:
((tea #t) (cup #t) (#f #t))
I would have thought that the answer would have been:
(((tea cup) #t) (#f #t))
My reasoning being that the 'x' in (teacupo x) should have its conde go through all of its solutions first, and unify to the list of all of its solutions. But it appears that teacupo only gives up one of its solutions at a time. It confuses me because my interpretation of conde is that, using the rule it gives, is that you should go through the lines of conde, and after a line succeeds, pretend that it failed, refresh the variables and find the next line that succeeds. Given the way that the solution works, it seems like the conde in that code goes back to a successful line, then forcing the teacupo conde to fail and give up the next possible value. Again, I would have thought that the teacupo solution would give up all of its conde solutions in a list, and then move on in the outer conde call. Can anyone provide me guidance as to why it works as provided in the book and not in the way I reasoned?
My reasoning being that the 'x' in (teacupo x) should have its conde
go through all of its solutions first, and unify to the list of all of
its solutions.
The variable x is unified to one value at a time.
The form (run* (x) <goals>) collects values for x which fulfill the goals.
> (run* (x)
(teacupo x))
'(tea cup)
In
(conde
((teacupo x) (== #t y))
((== #f x) (== #t y)))
there is two ways to succeed: either the goal (teacupo x) is met and x is one of tea or cup -- or -- the goals (== #f x) is met, and x is (unified to) #f.
In short run* runs through the possible possible value for x one at time collecting those values that meets all goals. This means that x is unified to one value at a time.
A simpler example:
> (run* (x)
(fresh (y)
(== y 10)
(conde
[(== x 1) (== y 10)]
[(== x 2) (== y 20)]
[(== x 3) (== y 10)])))
'(1 3)
Full code for those who want to try the snippets in DrRacket:
#lang racket
(require minikanren)
(define-syntax succeed (syntax-id-rules () [_ (fresh (t) (== t t))]))
(run* (x)
(fresh (y)
(== y 10)
(conde
[(== x 1) (== y 10)]
[(== x 2) (== y 20)]
[(== x 3) (== y 10)])))
(define teacupo
(lambda (x)
(fresh (result)
(conde
((== 'tea x ) succeed)
((== 'cup x ) succeed)))))
(run* (x)
(teacupo x))
(run* (r)
(fresh (x y)
(conde
((teacupo x) (== #t y))
((== #f x) (== #t y)))
(== (cons x (cons y '())) r)))
(teacupo x) means "succeed twice: once with x unified with tea, and the second time with x unified with cup". Then,
(conde
((teacupo x) (= #t y) #s)
((= #f x) (= #t y)) ; you had a typo here
(else #u)
means,
for each solution produced by (teacupo x), also unify y with #t and succeed; and also
for each solution produced by (= #f x), also unify y with #t and succeed; and also
produce no more solutions
So each x in (tea cup) is paired up with y in (#t), and also x in (#f) is paired up with y in (#t), to form r; and then r is reported, i.e. collected into the final result list of solutions, giving ( (tea #t) (cup #t) (#f #t) ).
"it appears that teacupo only gives up one of its solutions at a time."
yes, this is exactly right, conceptually.
"after a line succeeds, pretend that it failed, refresh the variables and find the next line that succeeds."
yes, but each line can succeed a multiple number of times, if the conditional (or a subsequent goal) succeeds a multiple number of times.
"it seems like the conde in that code goes back to a successful line, then forcing the teacupo conde to fail and give up the next possible value."
it actually prepares to produce them in advance (but as a stream, not as a list), and then each is processed separately, fed through the whole chain of subsequent steps until either the last step is reached successfully, or the chain is broken, cut short by an #u or by an otherwise failed goal. So the next one is tried when the processing of a previous one has finished.
In pseudocode:
for each x in (tea cup):
for each y in (#t): ; does _not_ introduce separate scope for `y`;
collect (x y) ; `x` and `y` belong to the same logical scope
for each x in (#f): ; so if `x` is used in the nested `for` too,
for each y in (#t): ; its new value must be compatible with the
collect (x y) ; one known in the outer `for`, or else
for each _ in (): ; it will be rejected (x can't be two different
collect (x y) ; things at the same time)
As to why does it work this way, I can point you to another answer of mine, which might be of help (though it doesn't use Scheme syntax).
Using it, as an illustration, we can write your test as a Haskell code which is actually functionally equivalent to the book's code I think (in this specific case, of course),
data Val = Fresh | B Bool | S String | L [Val] deriving Show
type Store = [(String,Val)]
teacupo x = unify x (S "tea") &&: true -- ((= tea x ) #s)
||: unify x (S "cup") &&: true -- ((= cup x ) #s)
||: false -- (else #u)
run = [[("r", Fresh)]] -- (run* (r) ......
>>: (\s -> [ s ++: [("x", Fresh), ("y", Fresh)] ]) -- (fresh (x,y)
>>: -- (conde
( teacupo "x" &&: unify "y" (B True)
&&: true -- ((teacupo x) (= #t y) #s)
||: unify "x" (B False) &&: unify "y" (B True) -- ((= #f x) (= #t y))
||: false -- (else #u)
)
&&: project ["x", "y"] (unify "r" . L) -- (= r (list x y))
>>:
reporting ["r"] -- ...... )
reporting names store = [[a | a#(n,_) <- store, elem n names]]
with bare minimum implementation, just enough to make the above code work,
project vars kont store =
kont [val | var <- vars, (Just val) <- [lookup var store]] store
unify :: String -> Val -> Store -> [Store]
unify sym val store =
let
(Just v) = (lookup sym store)
in
case (val_unify v val) of
Just newval -> [replace_val sym newval store] -- [updated store], if unifies
Nothing -> [] -- couldn't unify - reject it
val_unify v Fresh = Just v -- barely working,
val_unify Fresh v = Just v -- initial
val_unify (B v) (B u) | v == u = Just (B v) -- implementation
| otherwise = Nothing
val_unify (S v) (S u) | v == u = Just (S v)
| otherwise = Nothing
val_unify _ _ = Nothing
replace_val s n ((a,b):c) | s == a = (a,n) : c
| otherwise = (a,b) : replace_val s n c
producing the output
*Main> run
[[("r", L [S "tea",B True])], [("r", L [S "cup",B True])], [("r", L [B False,B True])]]
And if we change the second line in the translated conde expression to
||: unify "x" (B False) &&: unify "x" (B True) -- ((= #f x) (= #t x))
we indeed get only two results,
*Main> run
[[("r", L [S "tea",B True])], [("r", L [S "cup",B True])]]
I'm making a game, and I have this:
(define b "black piece") (define w "white piece")
(define (board)
(lambda (matrix)
(list ((b w b w b w b w)
(w b w b w b w b)
(b w b w b w b w)
(w b w b w b w b)
(b w b w b w b w)
(w b w b w b w b)
(b w b w b w b w)
(w b w b w b w b)))))
board makes a list with 8 lines and 8 columns of black and white pieces.
How do I access and change elements of the board? How do I do the procedure matrix with recursion?
first a few notes:
(define f (lambda (x) l ))
is the same as
(define (f x) l ))
You however are combining them with
(define (board) (lambda (matrix) l ))
which is the same as
(define board (lambda () (lambda (matrix) l )))
The distinction is important. The first two I have listed bind f to a function that take one parameter and return l. I'm guessing this is what you want to do. In the second two, you're binding board to a function that takes no parameters and returns a function that takes 1 parameter, matrix, (which it doesn't seem to do anything with), and returns a l.
second issue, (list ((b w....) ...)) isn't going to work because it will try to evaluate (b w ...). you need to have list in the function application position for each row of your board like so (list (list b w ...) (list w b ...) ...) in order for you code to even compile.
On to your question. link-ref is included in racket/base and is used for referencing elements in a list when you know the index into the list.
(list-ref 2 (list 'a 'b 'c 'd))
will return 'c. The index starts at 0. Since you have a list of lists, you will need to apply list-ref twice to retrieve a 'b or 'w.
As for changing it, well, you can't. As of r6rs, pairs (which make up lists) are immutable. The recommended way of doing things when possible is to return a new list with your change. you can use this somewhat inefficient version of list-set which returns a copy of the list with your new value at an index.
(define (list-set lis idx val)
(map (lambda (e i)
(if (= i idx) val e))
lis
(iota (length lis))))
In this case however, I would recommend switching to a different data structure more appropriate to the task at hand since you probably want O(1) access to the elements in the board. Look into vectors which behave much like lists but are used for constant lookups and updates. there is a built in vector-ref and vector-set! operations, which you should use instead of my above function.
Incase this is part of a larger problem and you're already using lists everywhere, you can use the vector->list and list->vector functions to go back and forth. Also, you can use mutable lists but don't.
Better still is the multidimensional array library offered in srfi/25, but that might be more complicated that you want to get.
The second part of your question was how to construct the board recursively. Well, here's a version using map.
(require (lib "1.ss" "srfi"))
(define (board)
(map (lambda (x)
(map (lambda (y)
(if (odd? (+ x y)) b w))
(iota 8)))
(iota 8)))
and here's a recursive version
(define (board)
(letrec ((board-helper
(lambda (x)
(if (eq? x 8) '()
(cons (row-helper x 0) (board-helper (+ 1 x))))))
(row-helper
(lambda (x y)
(if (eq? y 8) '()
(cons (if (odd? (+ x y)) b w) (row-helper x (+ 1 y)))))))
(board-helper 0)))