|
|
 |
 |
 |
 |
Scheme Programming Language
|
 |
 |
 |
 |
 |
 |
 |
 |
newbie - remembering state (using let) using paramterless procedure
Hi all, The problem: design a procedure called USHER that takes no params. Each time it is called, it checks to see if there are any seats left. If there are, the procedure returns a ticket number otherwise "Sorry. No seats left." The number of seats must be contained inside this procedure - not accessible to other procedures. Expected results: (usher) ; returns 1 (usher) ; returns 2 (usher) ; returns 3 (usher) ; returns "Sorry, sold out!" (usher) ; returns "Sorry, sold out!" I am clueless about HOW to approach this problem. I can't get past this issue: logically, I would expect that each time a parameterless procedure was called then all local variables inside its environment would be reinitialised. I am told it can be done - but have seen no code examples that show the technique. For me, this has been a really interesting issue of scoping variables - and at a guess, I'd say my problem lies in where I placed the variable needing to change. Of course, I am most likely completely wrong :) Could someone kindly nudge me in the right direction? My code is below. Kindest regards, Mike ;-------------- ;This is what I have done (does not work): (define (usher) (let ((max-seats 3) (last-ticket 0)) (if (equal? last-ticket max-seats) "Sorry Sold Out." (begin (set! last-ticket (+ last-ticket 1)) last-ticket)))) ; Each time the procedure is called the value for ; last-ticket is reinitialised to 0 and the result always 1. ; This is a snap if the number of available seats was external. (define seats 3) (define next-ticket 0) (define (usher) (if (equal? seats next-ticket) "Sorry, sold out!" (begin (set! next-ticket (+ next-ticket 1)) next-ticket ))) ; I tried an different approach ; (building a string on each call) and it too fails. ; (define (build-string) (let [[string-so-far ""]] (define accumulate-strings (let [[temp-string string-so-far]] (define [add-string new-string] (begin (set! temp-string (string-append temp-string new-string)) temp-string)) add-string)) (begin (set! string-so-far (string-append string-so-far (accumulate-strings "a"))) string-so-far))) (build-string) ; returns "a" (build-string) ; returns "a" (build-string) ; returns "a"
"Mike" <mcu87 @bigpond.net.au> writes: > Hi all, > The problem: design a procedure called USHER that takes no > params. Each time it is called, it checks to see if there > are any seats left. If there are, the procedure returns a > ticket number otherwise "Sorry. No seats left." The number > of seats must be contained inside this procedure - not > accessible to other procedures. > Expected results: > (usher) ; returns 1 > (usher) ; returns 2 > (usher) ; returns 3 > (usher) ; returns "Sorry, sold out!" > (usher) ; returns "Sorry, sold out!" > I am clueless about HOW to approach this problem. I can't > get past this issue: logically, I would expect that each > time a parameterless procedure was called then all local > variables inside its environment would be reinitialised. I > am told it can be done - but have seen no code examples that > show the technique. > For me, this has been a really interesting issue of scoping > variables - and at a guess, I'd say my problem lies in where > I placed the variable needing to change. Of course, I am > most likely completely wrong :) > Could someone kindly nudge me in the right direction?
Yes, just do it as you say! It's indeed a question of scope.
> My code is below. > Kindest regards, > Mike > ;-------------- > ;This is what I have done (does not work): > (define (usher) > (let ((max-seats 3) > (last-ticket 0)) > (if (equal? last-ticket max-seats) > "Sorry Sold Out." > (begin > (set! last-ticket (+ last-ticket 1)) > last-ticket)))) > ; Each time the procedure is called the value for > ; last-ticket is reinitialised to 0 and the result always 1.
So you know that when you put the variable inside the function it gets reinitialized everytime you enter the function. Therefore you need to put the variable outside of the function, as in: > ; This is a snap if the number of available seats was > external. > (define seats 3) > (define next-ticket 0) > (define (usher) > (if (equal? seats next-ticket) > "Sorry, sold out!" > (begin > (set! next-ticket (+ next-ticket 1)) > next-ticket > )))
Or, if you want a local variable, use let, but put the function inside: C/SCHEME[141]> (define usher (let ((seats 3) (next-ticket 0)) (lambda () (if (= seats next-ticket) "Sorry, sold out!" (begin (set! next-ticket (+ 1 next-ticket)) next-ticket))))) usher defined. C/SCHEME[142]> (usher) 1 C/SCHEME[143]> (usher) 2 C/SCHEME[144]> (usher) 3 C/SCHEME[145]> (usher) "Sorry, sold out!" C/SCHEME[146]> -- __Pascal Bourguignon__ http://www.informatimago.com/ NOTE: The most fundamental particles in this product are held together by a "gluing" force about which little is currently known and whose adhesive power can therefore not be permanently guaranteed.
Mike wrote: > Hi all, > The problem: design a procedure called USHER that takes no > params. Each time it is called, it checks to see if there > are any seats left. If there are, the procedure returns a > ticket number otherwise "Sorry. No seats left." The number > of seats must be contained inside this procedure - not > accessible to other procedures. > Expected results: > (usher) ; returns 1 > (usher) ; returns 2 > (usher) ; returns 3 > (usher) ; returns "Sorry, sold out!" > (usher) ; returns "Sorry, sold out!" > I am clueless about HOW to approach this problem. I can't > get past this issue: logically, I would expect that each > time a parameterless procedure was called then all local > variables inside its environment would be reinitialised. I > am told it can be done - but have seen no code examples that > show the technique.
Construct a function which, when evaluated, returns the function you want. Call that function with an argument giving the number of seats. Then, because the function you want is local to the function that defined it, in lexical scope, it can see the argument of the function that created it. Here's a way. (define usher (lambda (numseats) (lambda () (if (<= numseats 0) "sorry, sold out!" (begin (set! numseats (- numseats 1)) numseats))) 3) Now, the way you read this is: (define usher (anonymous-function 3)) Usher is the result of calling the anonymous function with an argument of 3. Not coincidentally, there's a form "let" defined for convenience that does this same trick. In order to use it, you'd write: (define usher (let ((numseats 3)) ;; let defines the anonymous ;; function from above and calls ;; it with argument 3 ... code for usher ... ) ) But I prefer using "lambda" to explain it because it's clearer what "lambda" does. Bear
Hi Pascall, Your solution makes perfect sense ... This is really amazing to me :) Silly, no? Until I saw this I would not have believed it possible but it really makes perfect sense. Light bulbs going off - that lambda expression could do a lot of different things if I wanted. For example, by adding a flag msg to the local variable list, I could tell how many times the procedure had been called and then branch.. (define usher (let ((seats 3) (next-ticket 0) (msg 0)) (lambda () (cond [(equal? msg 2) 'Go-away!] [else (if (= seats next-ticket) "Sorry, sold out!" (begin (set! next-ticket (+ 1 next-ticket)) (set! msg (+ msg 1)) next-ticket))])))) Embarrassingly, I was closest on my first attempt. I could not manage to get the code to work when I named the lambda expression (force of habit). My broken code is below. This returned 1 each time usher was called. (same as the code I first presented). It's not what I wanted but what I expected - frustrating. Stepper is great for seeing what is happening inside the code. In your solution the let statement is processed once - at the time usher is defined. Each time usher is applied, the lambda expression is evaluated - the let statement is not touched again. Aside from habit, another reason I used the approach (below) was to see if I might add a degree of flexibility with a pram tickets-required in the named procedure (might want to give more than one ticket). BTW - the code didn't check if tickets-required resulted in a negative ... could not get it to work with a param value of 1 :) Can you correct my thoughts on your approach? Your code does not enclose usher in brackets - it doesn't need to because it contains a lambda expression which is the procedure applied when usher is called. This explains WHY usher needed to be enclosed in brackets. However if I do this: (define foo (* 1 2 3 4)) then foo is a constant (assuming I do not redefine it - bad technique). I cannot enclose foo in brackets any more than I could (10) - generates an error. In both instances, the existence of brackets denotes that a procedure is to be applied - and instead of a procedure I gave foo (containing the value 24) and the numeric constant 10. Is this correct? The purpose of the exercise was to isolate the environment inside usher from being accessed externally, while still allowing usher to 'remember' it's internal state (previous value). My code (below) always caused the local variables to be initialised - and I can see why. Is there any way to name the lambda expression - and potentially add params (tickets-required might be the value of another procedure for example)? Just curious. I was aware of using (unnamed) lambda expressions - and until now, considered this to be convenience as opposed to requirement. Or is my thinking well off - as in, the lambda expression in your code is named and is called usher? Kindest regards, Mike --------- (define (usher) (let ((seats 3) (next-ticket 0)) (define (give-ticket tickets-required) (if (= seats next-ticket) "Sorry! Sold out." (begin (set! next-ticket (+ next-ticket tickets-required)) next-ticket))) (give-ticket 1))) "Pascal Bourguignon" <p@informatimago.com> wrote in message news:87myzjaxws.fsf@thalassa.lan.informatimago.com... | "Mike" <mcu87 @bigpond.net.au> writes: | | > Hi all, | > | > The problem: design a procedure called USHER that takes no | > params. Each time it is called, it checks to see if there | > are any seats left. If there are, the procedure returns a | > ticket number otherwise "Sorry. No seats left." The number | > of seats must be contained inside this procedure - not | > accessible to other procedures. | > | > Expected results: | > (usher) ; returns 1 | > (usher) ; returns 2 | > (usher) ; returns 3 | > (usher) ; returns "Sorry, sold out!" | > (usher) ; returns "Sorry, sold out!" | > | > I am clueless about HOW to approach this problem. I can't | > get past this issue: logically, I would expect that each | > time a parameterless procedure was called then all local | > variables inside its environment would be reinitialised. I | > am told it can be done - but have seen no code examples that | > show the technique. | > | > For me, this has been a really interesting issue of scoping | > variables - and at a guess, I'd say my problem lies in where | > I placed the variable needing to change. Of course, I am | > most likely completely wrong :) | > | > Could someone kindly nudge me in the right direction? | | Yes, just do it as you say! It's indeed a question of scope. | | | > My code is below. | > | > Kindest regards, | > | > Mike | > ;-------------- | > ;This is what I have done (does not work): | > | > (define (usher) | > (let ((max-seats 3) | > (last-ticket 0)) | > (if (equal? last-ticket max-seats) | > "Sorry Sold Out." | > (begin | > (set! last-ticket (+ last-ticket 1)) | > last-ticket)))) | > ; Each time the procedure is called the value for | > ; last-ticket is reinitialised to 0 and the result always 1. | | So you know that when you put the variable inside the function it gets | reinitialized everytime you enter the function. Therefore you need to | put the variable outside of the function, as in: | | | > ; This is a snap if the number of available seats was | > external. | > | > (define seats 3) | > (define next-ticket 0) | > (define (usher) | > (if (equal? seats next-ticket) | > "Sorry, sold out!" | > (begin | > (set! next-ticket (+ next-ticket 1)) | > next-ticket | > ))) | | Or, if you want a local variable, use let, but put the function inside: | | C/SCHEME[141]> (define usher (let ((seats 3) | (next-ticket 0)) | (lambda () | (if (= seats next-ticket) | "Sorry, sold out!" | (begin | (set! next-ticket (+ 1 next-ticket)) | next-ticket))))) | usher defined. | C/SCHEME[142]> (usher) | 1 | C/SCHEME[143]> (usher) | 2 | C/SCHEME[144]> (usher) | 3 | C/SCHEME[145]> (usher) | "Sorry, sold out!" | C/SCHEME[146]> | | | -- | __Pascal Bourguignon__ http://www.informatimago.com/ | | NOTE: The most fundamental particles in this product are held | together by a "gluing" force about which little is currently known | and whose adhesive power can therefore not be permanently | guaranteed.
Hi Bear, Thank you for taking the time to help - I really appreciate it. I just responded to Pascall - silly I know, but this is exciting stuff. I'd not have considered doing anything like this at all ... to abstract perhaps. But now that I see it, it's as obvious as my nose on my own butt :) I'm afraid I was unable to get your code to work - perhaps my mistake. I *think* I see where you are going with it though. Please correct me if I am wrong, (I was unable to step through the code). The inner lambda expression is primed with the number of seats to start with. However, it seems that the code can only ever return 3, the inner lambda expression is not applied. And usher needs to be fed a param that, irrespective returns 3. (usher "hello") ;==> 3. I am very grateful for any help - and don't expect anyone to solve things for me ... I would have expected that the value 3 (in your code) IS supposed to prime numseats. Need to play with this to see where it leads. I really haven't used naked lambda expressions all that much - and until now clearly I was mistaken about how to exploit this. Nothing has been lost - rather, a whole load of opportunities have opened up that I had never known existed until now. This is really a fun language. Kindest regards, Mike "Ray Dillinger" <b @sonic.net> wrote in message news:4660dbee$0$27255$742ec2ed@news.sonic.net... [....snip..] | > | > The problem: design a procedure called USHER that takes no | > params. Each time it is called, it checks to see if there | > are any seats left. If there are, the procedure returns a | > ticket number otherwise "Sorry. No seats left." The number | > of seats must be contained inside this procedure - not | > accessible to other procedures. | > | > Expected results: | > (usher) ; returns 1 | > (usher) ; returns 2 | > (usher) ; returns 3 | > (usher) ; returns "Sorry, sold out!" | > (usher) ; returns "Sorry, sold out!" [.........snip.....]| | Construct a function which, when evaluated, returns the | function you want. Call that function with an argument | giving the number of seats. | | Then, because the function you want is local to the function | that defined it, in lexical scope, it can see the argument | of the function that created it. | | Here's a way. | | (define usher | (lambda (numseats) | (lambda () | (if (<= numseats 0) | "sorry, sold out!" | (begin | (set! numseats (- numseats 1)) | numseats))) 3) | | Now, the way you read this is: | | (define usher | (anonymous-function 3)) | | Usher is the result of calling the anonymous function | with an argument of 3. | | Not coincidentally, there's a form "let" defined for | convenience that does this same trick. In order to | use it, you'd write: | | (define usher | (let ((numseats 3)) ;; let defines the anonymous | ;; function from above and calls | ;; it with argument 3 | ... code for usher ... | ) ) | | But I prefer using "lambda" to explain it because it's | clearer what "lambda" does. | | Bear | | |
Mike wrote: > Hi Bear, > I'm afraid I was unable to get your code to work - perhaps > my mistake. I *think* I see where you are going with it > though. Please correct me if I am wrong, (I was unable to > step through the code). The inner lambda expression is > primed with the number of seats to start with. However, it > seems that the code can only ever return 3, the inner lambda > expression is not applied. And usher needs to be fed a > param that, irrespective returns 3. (usher "hello") ;==> 3.
Oops, my mistake. I shouldn't post code without testing it, but sometimes I do. Okay, here's what I meant to post. (define usher ((lambda (numseats) ;;<== original version missed an ;; open paren before lambda (lambda () (if (<= numseats 0) "sorry, sold out!" (begin (set! numseats (- numseats 1)) numseats)))) 3) ;; <== and closing paren ;; before 3. As I look it over, I realize one thing: I have usher return the number of seats remaining, whereas you wanted a function that returns the number of seats so far sold. But you should be able to work this one out, right? It just wants another variable, with the same kind of scope as numseats, that keeps track of how many seats sold. So close over another variable, and you should be able to fix it yourself. :-) Bear
Hi Bear, Thanks heaps! This is the missing link - now my tool kit is getting truly interesting :) I learn lots by looking at code examples - and then seeing what I can do with them. I'm not quite 'there' yet in terms of evaluating the merit (efficiency) of one approach over another. For the moment, I do my best to get a thing to work - then see if I can make it better. And yes, I can take this and wreak loads of mischief with it ... all good of course. Crystal clear code is always easy to follow. Thank you again, Mike "Ray Dillinger" <b @sonic.net> wrote in message news:466138c9$0$27198$742ec2ed@news.sonic.net... | Mike wrote:
| > Hi Bear, | | > I'm afraid I was unable to get your code to work - perhaps | > my mistake. I *think* I see where you are going with it | > though. Please correct me if I am wrong, (I was unable to | > step through the code). The inner lambda expression is | > primed with the number of seats to start with. However, it | > seems that the code can only ever return 3, the inner lambda | > expression is not applied. And usher needs to be fed a | > param that, irrespective returns 3. (usher "hello") ;==> 3. | | Oops, my mistake. I shouldn't post code without testing | it, but sometimes I do. | | Okay, here's what I meant to post. | | (define usher | ((lambda (numseats) ;;<== original version missed an | ;; open paren before lambda | (lambda () | (if (<= numseats 0) | "sorry, sold out!" | (begin | (set! numseats (- numseats 1)) | numseats)))) 3) ;; <== and closing paren | ;; before 3. | | As I look it over, I realize one thing: I have usher | return the number of seats remaining, whereas you | wanted a function that returns the number of seats | so far sold. | | But you should be able to work this one out, right? | It just wants another variable, with the same kind | of scope as numseats, that keeps track of how many | seats sold. So close over another variable, and you | should be able to fix it yourself. :-) | | Bear |
"Mike" <mcu87 @bigpond.net.au> writes: > [...] > Can you correct my thoughts on your approach? Your code > does not enclose usher in brackets - it doesn't need to > because it contains a lambda expression which is the > procedure applied when usher is called. This explains WHY > usher needed to be enclosed in brackets. Actually, (define (f arg...) body...) is a shortcut for: (define f (lambda (arg...) body...)) > However if I do > this: (define foo (* 1 2 3 4))
And of course, apart from this shortcut, there's no difference in scheme between function literals (lambda) and other literals such as numbers. > then foo is a constant
No, just a variable bound to some number at that time. > (assuming I do not redefine it - bad technique). I cannot > enclose foo in brackets any more than I could (10) - > generates an error. In both instances, the existence of > brackets denotes that a procedure is to be applied - and > instead of a procedure I gave foo (containing the value 24) > and the numeric constant 10. Is this correct?
Indeed. In lisp, parentheses mean basically function _call_, so the first item of the list must evaluate to a function (or be a syntax or a macro). > [...] > Or is my thinking well off - as in, the lambda expression in > your code is named and is called usher?
Yes. What lambda produces is not a mere procedure, but a _closure_. That is, the free variables in the lambda expression are _enclosed_ into the closure environment for the procedure to access them when it's called. In general, these free variables are found in the global environment. But when they are lexical variables, they're just enclosed all the same, and the lexical scope is brought along with the procedure, making an independant closure. As indicated by Ray, you can create famillies of these closures, writting a function to generate them: (define make-usher (lambda (nb-of-seats) (let ((remaining-seats nb-of-seats)) (lambda () (if (< 0 remaining-seats) (begin (set! remaining-seats (- remaining-seats 1)) remaining-seats) "No more seats"))))) (define big-usher (make-usher 10)) (define small-usher (make-usher 3)) (list (small-usher) (small-usher) (small-usher) (small-usher) (big-usher) (big-usher) (big-usher) (big-usher)) --> (2 1 0 "No more seats" 9 8 7 6) ;; Note: the result may vary depending on your implemenation. ;; it could as well be: --> ("No more seats" 0 1 2 6 7 8 9) -- __Pascal Bourguignon__ http://www.informatimago.com/ NOTE: The most fundamental particles in this product are held together by a "gluing" force about which little is currently known and whose adhesive power can therefore not be permanently guaranteed.
|
 |
 |
 |
 |
|