Home     |     .Net Programming    |     cSharp Home    |     Sql Server Home    |     Javascript / Client Side Development     |     Ajax Programming

Ruby on Rails Development     |     Perl Programming     |     C Programming Language     |     C++ Programming     |     IT Jobs

Python Programming Language     |     Laptop Suggestions?    |     TCL Scripting     |     Fortran Programming     |     Scheme Programming Language


 
 
Cervo Technologies
The Right Source to Outsource

MS Dynamics CRM 3.0

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...

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:
> 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.

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

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.

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:

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.

Add to del.icio.us | Digg this | Stumble it | Powered by Megasolutions Inc