|
|
 |
 |
 |
 |
Scheme Programming Language
|
 |
 |
 |
 |
 |
 |
 |
 |
Can you change the values in a defined list?
Hi all, Still learning :) In other languages, you can easily change the values contained in an object. So far as I can tell, the convention in Scheme is to define constants using the DEFINE keyword and local variables using SET. My question is most likely entirely silly but ... nothing ventured ... and as always, please feel free to correct my thoughts. (define a-list (list 3 0)); create a constant of type list. (define (some-proc a-list) ; values in object a-list passed to procedure (if (= 0 (first a-list)) (+ 1 (first a-list)) (+ 2 (second a-list))) ) (some-proc a-list) ; ===> the value 2 In the above, a-list values remain unchanged. Even though I used a-list in the procedure, I'm not really 'touching' it because it is not a-list that is directly passed but the values contained in a-list. The result produced by some-proc are assigned to the object some-proc. So far as I can tell, I cannot alter any value in a-list itself within a procedure however I can redefine it after the procedure like this: (define a-list (list 3 2)). But seems an inappropriate thing to do to a constant (assuming this is the way Scheme creates constants) and generally poor coding technique. The reason I am assuming that (define foo 1) is how Scheme creates constants is that this: (define foo a-list) will create a 'constant' containing the procedure a-list (if written below the code above). foo ;==> returns (3 #<procedure:some-proc>). Not exactly a 'constant' in the strict sense of a non-changing value (constant) but one containing a procedure whose address is constant (again, an assumption) but might hold different values depending upon how the procedure resolves. Suppose a-list contained two values representing dollars and cents, and a process resolves to a new list with different values. If I wanted to reflect those changes in a-list, I can't see any way to do it (excluding using SET!). As I said earlier, if a-list is the Scheme equivalent of a constant I don't think it is good technique to do this but I was wondering if it were possible to do. So, how do you change the values in a-list (above example) without using SET! and is using SET! a 'good' thing or a work-around the restriction of the defined constant? Be interested in your thoughts. Kindest regards, Mike
>>>>> "Mike" == Mike <mcu87 @bigpond.net.au> writes: Mike> So far as I can tell, the convention in Scheme is to define Mike> constants using the DEFINE keyword and local variables using Mike> SET. Not really. There is no such thing as a constant; you use "define" for any kind of object. You use set! to make an existing variable contain a new value. Mike> (define a-list (list 3 0)); create a constant of type list. No; it merely defines a list. It's not constant. Mike> (define (some-proc a-list) ; values in object a-list passed to procedure Mike> (if (= 0 (first a-list)) Mike> (+ 1 (first a-list)) Mike> (+ 2 (second a-list))) Mike> ) Mike> (some-proc a-list) ; ===> the value 2 Mike> In the above, a-list values remain unchanged. Correct. Mike> So far as I can tell, I cannot alter any value in a-list Mike> itself within a procedure You've overlooked set-car! and set-cdr!, which do exactly what you want. > (define a-list (list 3 0)) > a-list (3 0) > (define (some-other-proc a-list) (set-car! a-list 'three) (set-car! (cdr a-list) 'zee-row )) > a-list (3 0) > (some-other-proc a-list) > a-list (three zee-row) > -- "Hoot" has its heart in the right place, but I have been unable to locate its brain. -- Roger Ebert
Mike writes: > In other languages, you can easily change the values contained in an
You can change a list with set-car!, which stores a new value in the first element slot in the list. To support names like "first" and "second" that you seem to like, just define auxiliaries: (define (set-first! list val) (set-car! list val)) (define (set-second! list val) (set-first! (rest list) val)) Then you can observe: (define triple (list 3 1 4)) (set-first! triple 2) (set-second! triple 7) (set-second! (rest triple) 2) triple ==> (2 7 2) We don't usually do this. It tends to make programs a lot harder to understand.
Hi Eric, Thanks for this. When I'm tinkering around I use multiple DEFINE's to see if I can break the code so I knew I could do it that way. Scheme is not just a teaching language, is it? I had heard that some LINUX utilities were written in it ... not too certain though. Actually, the more I use it the more I really love it. Do people who use it for commercial / development purposes stick to any specific coding conventions? Guess I'm looking for a framework in a way. BTW - yes, I did over-look set-car! and set-cdr! ... my bad. Doh! Mike "Eric Hanchrow" <off @blarg.net> wrote in message news:871wiydl1l.fsf@offby1.atm01.sea.blarg.net...
>>>>>> "Mike" == Mike <mcu87 @bigpond.net.au> writes: > Mike> So far as I can tell, the convention in Scheme is to define > Mike> constants using the DEFINE keyword and local variables using > Mike> SET. > Not really. There is no such thing as a constant; you use "define" > for any kind of object. You use set! to make an existing variable > contain a new value. > Mike> (define a-list (list 3 0)); create a constant of type list. > No; it merely defines a list. It's not constant. > Mike> (define (some-proc a-list) ; values in object a-list passed to > procedure > Mike> (if (= 0 (first a-list)) > Mike> (+ 1 (first a-list)) > Mike> (+ 2 (second a-list))) > Mike> ) > Mike> (some-proc a-list) ; ===> the value 2 > Mike> In the above, a-list values remain unchanged. > Correct. > Mike> So far as I can tell, I cannot alter any value in a-list > Mike> itself within a procedure > You've overlooked set-car! and set-cdr!, which do exactly what you want. > > (define a-list (list 3 0)) > > a-list > (3 0) > > (define (some-other-proc a-list) > (set-car! a-list 'three) > (set-car! (cdr a-list) 'zee-row )) > > a-list > (3 0) > > (some-other-proc a-list) > > a-list > (three zee-row) > -- > "Hoot" has its heart in the right place, but I have been > unable to locate its brain. > -- Roger Ebert
Thanks Jussi, I did over-look set-car! and set-cdr! Never ceases to amaze me how easy it is to alter the programming environment in Scheme. So in the 'real world' use of Scheme, it is not considered poor technique to liberally alter variables / constants created using define? From a personal perspective, I guess I prefer to avoid it if possible so my code is easier to read and maintain. BTW - your example actually frees me up to make use of old habits again. If I want to do something like INSTR then write it myself. Good way to learn as well. Regards, Mike "Jussi Piitulainen" <jpiit @ling.helsinki.fi> wrote in message news:qotodm27yjy.fsf@venus.ling.helsinki.fi...
> Mike writes: >> In other languages, you can easily change the values contained in an > You can change a list with set-car!, which stores a new value in the > first element slot in the list. To support names like "first" and > "second" that you seem to like, just define auxiliaries: > (define (set-first! list val) > (set-car! list val)) > (define (set-second! list val) > (set-first! (rest list) val)) > Then you can observe: > (define triple (list 3 1 4)) > (set-first! triple 2) > (set-second! triple 7) > (set-second! (rest triple) 2) > triple ==> (2 7 2) > We don't usually do this. It tends to make programs a lot harder to > understand.
Mike writes: > So in the 'real world' use of Scheme, it is not considered poor > technique to liberally alter variables / constants created using > define? From a personal perspective, I guess I prefer to avoid it > if possible so my code is easier to read and maintain.
You can go far in Scheme without set! and without set-car! and ilk, but when you need them, they are available. Use with care. Your preference is good.
On Apr 6, 1:22 am, Eric Hanchrow <off@blarg.net> wrote: > >>>>> "Mike" == Mike <mcu87 @bigpond.net.au> writes: > Mike> So far as I can tell, the convention in Scheme is to define > Mike> constants using the DEFINE keyword and local variables using > Mike> SET. > Not really. There is no such thing as a constant;
I have a different but related question. In the R5RS section on quote: "it is an error to alter a constant (i.e. the value of a literal expression) using a mutation procedure like set-car! or string-set!." There seems to be differing interpretations of this. Mzscheme, petite chez, gambit allow you to mutate a quoted list, but scheme48 does not. If scheme48 the only one of the four that got this right, why do the others treat a quoted list the same as any other list. Or is scheme48 wrong? Brad
nottysym @yahoo.ca writes: > I have a different but related question. In the R5RS section on > quote: "it is an error to alter a constant (i.e. the value of a > literal expression) using a mutation procedure like set-car! or > string-set!." > There seems to be differing interpretations of this. Mzscheme, > petite chez, gambit allow you to mutate a quoted list, but scheme48 > does not. If scheme48 the only one of the four that got this right, > why do the others treat a quoted list the same as any other list. Or > is scheme48 wrong?
"It is an error" means only that an implementation is allowed to signal an error. It is also allowed to do something unexpected. None of those implementations are wrong, but you can only rely on their behaviour if they promise something in their own documentation, outside R5RS.
On Apr 6, 9:49 am, Jussi Piitulainen <jpiit@ling.helsinki.fi> wrote: > > There seems to be differing interpretations of this. Mzscheme, > > petite chez, gambit allow you to mutate a quoted list, but scheme48 > > does not. If scheme48 the only one of the four that got this right, > > why do the others treat a quoted list the same as any other list. Or > > is scheme48 wrong? > "It is an error" means only that an implementation is allowed to > signal an error. It is also allowed to do something unexpected. None > of those implementations are wrong, but you can only rely on their > behaviour if they promise something in their own documentation, > outside R5RS.
I see that I was reading that little snippet of R5RS out of context. I'm still curious about why an implementor would choose not to signal an error. Would it be 1. efficiency -- too much burden on the type system to check for mutable/immutable 2. it's always been that way -- changing the behaviour now would break existing code. 3. convenience -- it's just easier for people to write '(1 2 3) than (list 1 2 3) and expect it behave the same way. 4. some other reason. Brad
>>>>> "Mike" == Mike <mcu87 @bigpond.net.au> writes: Mike> Scheme is not just a teaching language, is it? In practice, I suspect that's close to true. Many Scheme lovers will contradict me, but I don't know of commercial software written in scheme, nor widely used software that ships with any OS. Mike> I had heard that some LINUX utilities were written in it ... Certainly none of the common ones. -- If there were a little guy running around inside the computer executing our programs, he would probably have as long and plaintive a tale to tell about his job as a federal government employee. -- Paul Graham
nottysym @yahoo.ca writes: > I'm still curious about why an implementor would choose not to > signal an error. Would it be > 1. efficiency -- too much burden on the type system to check for > mutable/immutable > 2. it's always been that way -- changing the behaviour now would > break existing code. > 3. convenience -- it's just easier for people to write '(1 2 3) > than (list 1 2 3) and expect it behave the same way. > 4. some other reason.
My guess is mainly 1 and 4, the other reason being implementation effort. Maybe some implementors also prefer to guarantee 3, I don't know.
Jussi Piitulainen wrote: > nottysym @yahoo.ca writes: > > I'm still curious about why an implementor would choose not to > > signal an error. Would it be > > 1. efficiency -- too much burden on the type system to check for > > mutable/immutable > > 2. it's always been that way -- changing the behaviour now would > > break existing code. > > 3. convenience -- it's just easier for people to write '(1 2 3) > > than (list 1 2 3) and expect it behave the same way. > > 4. some other reason. > My guess is mainly 1 and 4, the other reason being implementation > effort.
Reason 4 is that many implementations of Lisp and Scheme do not have any bits to spare in their representations of pairs, so enforcement of immutability for pairs would involve adding headers to pairs (which would increase their space by 50%), rewriting the memory manager to keep immutable pairs in a special region (which might make allocation/gc less efficient), or doing something even less efficient such as recording all immutable pairs within a hash table and having the mutation procedures (set-car! and set-cdr!) make sure their argument isn't contained within that table. Given that some implementations of Scheme are lenient about enforcement of such things, while others are strict, I have concluded that Larceny should provide two modes of execution: a strict mode that checks for non-portable assumptions and rejects programs that make them, and a lenient mode that allows programs to run even if they violate some restrictions of the R5RS (or R6RS if such a thing comes to pass). You'd use the strict mode when testing your program for portability, and the lenient mode when running programs that aren't portable enough to run in strict mode. Larceny's strict mode will be much slower than lenient mode. Accepting that compromise right up front allows the strict mode to perform runtime checks that would be too expensive for systems that try to perform portability checking when executing production code; such systems inevitably end up with a compromise between portability testing and performance. Finally, let me point out that portability testing goes well beyond the level of runtime checking needed for safety; Larceny's lenient mode will still offer about the same level of safety as a JVM or the CLR. Will
William D Clinger writes: > Jussi Piitulainen wrote: >> nottysym @yahoo.ca writes: >>> I'm still curious about why an implementor would choose >>> not to signal an error. Would it be >>> 1. efficiency -- too much burden on the type system to check for >>> mutable/immutable ... >>> 4. some other reason. >> My guess is mainly 1 and 4, the other reason being >> implementation effort. > Reason 4 is that many implementations of Lisp and Scheme > do not have any bits to spare in their representations of > pairs, so enforcement of immutability for pairs would > involve adding headers to pairs (which would increase > their space by 50%), rewriting the memory manager to keep > immutable pairs in a special region (which might make > allocation/gc less efficient), or doing something even > less efficient such as recording all immutable pairs > within a hash table and having the mutation procedures > (set-car! and set-cdr!) make sure their argument isn't > contained within that table.
All those alternatives are efficiency concerns, so they fit reason 1 quite well. Some are mainly space efficiency, some are running time, but nottysym@yahoo.ca didn't make that distinction either. ... > Larceny's strict mode will be much slower than lenient > mode. Accepting that compromise right up front allows > the strict mode to perform runtime checks that would be > too expensive for systems that try to perform portability > checking when executing production code; such systems
That sounds interesting. Instead of simply rejecting non-portable programs, it would be more informative to write a report about what non-portable assumptions are found: help the user remove unintentional problems and document intentional ones.
nottysym @yahoo.ca writes: > On Apr 6, 1:22 am, Eric Hanchrow <off @blarg.net> wrote: >> >>>>> "Mike" == Mike <mcu87 @bigpond.net.au> writes: >> Mike> So far as I can tell, the convention in Scheme is to define >> Mike> constants using the DEFINE keyword and local variables using >> Mike> SET. >> Not really. There is no such thing as a constant; > I have a different but related question. In the R5RS section on quote: > "it is an error to alter a constant (i.e. the value of a literal > expression) using a mutation procedure like set-car! or string-set!." > There seems to be differing interpretations of this. Mzscheme, petite > chez, gambit allow you to mutate a quoted list, but scheme48 does not. > If scheme48 the only one of the four that got this right, why do the > others treat a quoted list the same as any other list. Or is scheme48 > wrong?
In addition too the other good answers, let me add that it is still an error, only you've not found it yet. With: (define (f x) (let ((a '(1 2 3))) (if (< x 0) a (begin (set-car! a x) a)))) On some implementation, you may have: (f 10) --> (10 2 3) (f -1) --> (10 2 3) ; did you expect that??? On some other: (f 10) --> (10 2 3) (f -1) --> (1 2 3) ; did you expect that??? And on the lucky ones: (f 10) --> error, modifying a literal car... -- __Pascal Bourguignon__ http://www.informatimago.com http://pjb.ogamita.org
On Apr 8, 9:17 am, Pascal Bourguignon <p@informatimago.com> wrote:
> (define (f x) > (let ((a '(1 2 3))) > (if (< x 0) > a > (begin > (set-car! a x) > a)))) > On some implementation, you may have: > (f 10) --> (10 2 3) > (f -1) --> (10 2 3) ; did you expect that??? > On some other: > (f 10) --> (10 2 3) > (f -1) --> (1 2 3) ; did you expect that??? > And on the lucky ones: > (f 10) --> error, modifying a literal car...
So, knowing that a is (supposed to be) a constant, an implementation might allocate it inline inside the body of f at compile time as an optimization. Others may allocate a fresh one on the heap for every invocation of f... interesting. Brad.
|
 |
 |
 |
 |
|