[comp.lang.pascal] Funny evaluation of functions

edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) (08/23/89)

   The following code produces 25 as output in Turbo Pascal 4.0.  Can
anyone explain what is going on?
 
  var y:integer;

  Function one(var x:integer):integer;
  begin
   x:=x+1;
   one:=x;
  end;
 begin
  y:=4;
  writeln(output,y*one(y));
 end.
   
   Edwin J. Kay
   CSEE
   Lehigh University
  

mketch@pawl.rpi.edu (Michael D. Ketchen) (08/24/89)

In article <614@lehi3b15.csee.Lehigh.EDU> edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) writes:
>
>   The following code produces 25 as output in Turbo Pascal 4.0.  Can
>anyone explain what is going on?

Well, I'll try...

>  var y:integer;
>
>  Function one(var x:integer):integer;
>  begin
>   x:=x+1;
>   one:=x;
>  end;
> begin
>  y:=4;
>  writeln(output,y*one(y));

Okay, when the compiler sees the expression "y*one(y)", it builds code to
evaluate it.  Now, from the output you gave (25), it appears that the 'y'
portion is stored as a pointer to y's variable location in memory.  Since
the parameter of one() is call-by-reference and is changed in the function,
y's value in memory is also changed.  Note that this is BEFORE the actual
evaluation of the expression.

So, you start with y=4.  The expression is encountered, and a pointer to y
is saved somewhere.  Then, one(y) is encountered, and a function call is
generated.  The first statement in one() is "x:=x+1;".  Since x is really a
pointer to y, y's value is changed to 5.  Then the return value of one() is
set to x (5), and the function returns.  Now the expression can be evaluated.
The program looks in y, sees 5, multiplies it by the return value of one(),
which is 5, and comes up with 25, which is what you see printed.

> end.
>   
>   Edwin J. Kay
>   CSEE
>   Lehigh University

- Mike
--
|XXX| __/\__ |XXX|--------------------------+-----------------********=========
|XXX| \    / |XXX|  Michael D. Ketchen      |   This space    ********=========
|XXX| /____\ |XXX|  mketch@pawl.rpi.edu     |   for rent...   =================
|XXX|   ][   |XXX|  mketch@rpitsmts.bitnet  +-----------------=================

gus@pyr.gatech.EDU (gus Baird) (08/24/89)

>Article 2362 of comp.lang.pascal:
>>From: edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay)
>Newsgroups: comp.lang.pascal
>Subject: Funny evaluation of functions
>Date: 23 Aug 89 15:50:10 GMT
>Organization: CSEE Dept. Lehigh University, Bethlehem, PA
>
>   The following code produces 25 as output in Turbo Pascal 4.0.  Can
>anyone explain what is going on?
> 
>  var y:integer;
>
>  Function one(var x:integer):integer;
>  begin
>   x:=x+1;
>   one:=x;
>  end;
> begin
>  y:=4;
>  writeln(output,y*one(y));
> end.
                                                                   
                                                                   
                                                                   
                                                                   

Huh?  Why shouldn't it give 25?  It looks to me like you TOLD
it to...
                                                                   
In "one", x is a variable parameter.  starts at (4).  
gets incremented to (5) as the first op in "one".  
Thenceforth y of main has value (5).
"one" returns the current value of (x of "one") == (y of main) = 5.
                                                                   
Functions get evaluated first, in expression precedence rules.
you get (5) back from "one(y)", multiply by (current value of y) = 5,
giving 25.
                                                                   
BTW, standard practice in Pascal shops is to not use variable
parameters in functions.  Usual pejorative phrase is "side effect".
                                                                   
                                                                   
                                                                   

-- 
gus Baird
School of ICS, Georgia Institute of Technology, Atlanta, Georgia, 30332
...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!gitpyr!gus

sean@eleazar.dartmouth.edu (Sean P. Nolan) (08/24/89)

I'm pretty sure that standard Pascal (and Turbo as well) evaluates expressions
from right to left. That is, your function is invoked FIRST, which changes y,
resulting in 5*5 as opposed to the "intuitive" 4*5 you intended.

Somebody presented a theory to me as to why this was implemented this way,
but I can't remember what it was and think it was stupid anyways. *grin*

--- Sean

+----------------------------------------------------------------------------+
| Sean P. Nolan     |                                   |  "Let's face it:   |
| Dartmouth College | Net: Sean_Nolan@Mac.Dartmouth.EDU |   IBM is no fun."  |
| Hinman Box 2658   | MCI Mail: snolan                  |     ::::::::::     |
| Hanover, NH 03755 |                                   |   John C. Dvorak   |
+----------------------------------------------------------------------------+

tom@stiatl.UUCP (Tom Wiencko) (08/24/89)

In article <614@lehi3b15.csee.Lehigh.EDU> edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) writes:
>>
>>   The following code produces 25 as output in Turbo Pascal 4.0.  Can
>>anyone explain what is going on?
>> 
>>  var y:integer;
>>
>>  Function one(var x:integer):integer;
>>  begin
>>   x:=x+1;
>>   one:=x;
>>  end;
>> begin
>>  y:=4;
>>  writeln(output,y*one(y));
>> end.

Sure.  You are changing the value of 'y' to 5 since the parameter to function
'one' is a var parameter.  If this side effect is not what you wanted, the
function should be written so:

	Function one(x:integer):integer;
	begin
	  one := x + 1; 	
        end;

This is, of course, the difference between passing values by reference (var) 
and passing them by value (as above).  You generally do not want to pass by
reference if you plan on monkeying with the passed variables unless you 
want the side effects.  Actually, I recommend to beginning programmers that
they not use passed parameters as working variables at all unless they are
sure they know what they are doing, to prevent just this sort of thing.

Side effects of passing variables by reference are horrible to find.  In this
case, because the compiler did not bother to get the 'y' value before it 
executed the 'one' function you got nailed.  Most compilers will do this - 
evaluate functions first, then fetch variables.  

Tom



-- 
Tom Wiencko                                            (w) (404) 977-4515
gatech!stiatl!tom                                  Wiencko & Associates, Inc.

fsbrn@BRL.MIL (VLD/LTTB) (08/24/89)

Haah,

Edwin J. Kay says:

>    The following code produces 25 as output in Turbo Pascal 4.0.  Can
> anyone explain what is going on?
>  
>   var y:integer;
> 
>   Function one(var x:integer):integer;
>   begin
>    x:=x+1;
>    one:=x;
>   end;
>  begin
>   y:=4;
>   writeln(output,y*one(y));
>  end.

I tried the program with Berkeley Pascal and got the same result.
Pascal doesn't guarantee the order in which an expression is
evaluated.  What is happening is the function "one" is being called
first, so "y" changes to 5, THEN the value returned by the function
(also 5) is multiplied by the value of "y".

1. eval one(y)
2. change y to 5
3. return 5
4. need value of y -> get 5
5. multiply numbers together and print 25

The order of the steps *may* have been 4,1,2,3,5 giving 20.

Remember that in Pascal you may not say
  var num [1..MAX] of integer;
  if (i <= MAX) AND (num[i] <> 17)
because the standard says the entire expression must be evaluated, but
also because it doesn't guarantee that the second expression won't be
evaluated before the first one (and because Pascal doesn't terminate
boolean expressions as soon as possible).

This example shows why side effects are a Bad Thing.  An optimizing
compiler may shuffle an expression so you don't know in what order the
subexpressions will be evaluated.  If the program said
  tmp := one(y);
  writeln(output,y*tmp);
there would be no confusion.

                                        dsw, fferd
                                        Fred S. Brundick
                                        USABRL, APG, MD.
                                        <fsbrn@brl.mil>

donnam@palomar.SanDiego.NCR.COM (Donna Mitchell) (08/24/89)

In article <614@lehi3b15.csee.Lehigh.EDU> edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) writes:
>
>   The following code produces 25 as output in Turbo Pascal 4.0.  Can
>anyone explain what is going on?
> 
	Using Turbo Pascal 3.0 it produces 20.

ags@mace.cc.purdue.edu (Dave Seaman) (08/24/89)

In article <20693@adm.BRL.MIL> fsbrn@BRL.MIL (VLD/LTTB) writes:
	[ You have all seen the funny program by now. ]
>Remember that in Pascal you may not say
>  var num [1..MAX] of integer;
>  if (i <= MAX) AND (num[i] <> 17)
>because the standard says the entire expression must be evaluated, but
>also because it doesn't guarantee that the second expression won't be
>evaluated before the first one (and because Pascal doesn't terminate
>boolean expressions as soon as possible).

It is not true that the entire expression must be evaluated.  The standard
says that an implementation MAY CHOOSE to evaluate the entire expression,
but it isn't required, in general.

In an expression like "x*one(x)", it would be possible to skip the
evaluation of the function "one" if x happens to be zero.  If "one" has
side effects, then the result is implementation-dependent.  Assuming that
both terms are evaluated, they may be taken in either order.  

880716a@aucs.uucp (Dave Astels) (08/24/89)

In article <20693@adm.BRL.MIL> fsbrn@BRL.MIL (VLD/LTTB) writes:
...
>Remember that in Pascal you may not say
>  var num [1..MAX] of integer;
>  if (i <= MAX) AND (num[i] <> 17)
>because the standard says the entire expression must be evaluated, but
>also because it doesn't guarantee that the second expression won't be
>evaluated before the first one (and because Pascal doesn't terminate
>boolean expressions as soon as possible).

Ahh!  Maybe in insufficiently capable 'standard' Pascal, but it is perfectly
ok in Turbo Pascal (ver 4 and up, anyway).  You can set boolean expressions
to be short circuited.  This makes the above code snipet quite reasonable.
Because of this, left to right evaluatioin is gaurenteed (it has to be in
order for it to work).

I've used standard Pascal on our Sun, and compared to Turbo (I'm using 5.5
now .. marvelous package) it seems prehistoric.   Hey, standard Pascal is
prehistoric, isn't it.


-- 
- Dave

Internet: 880716a@AcadiaU.CA
Bitnet:   880716a@Acadia

dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (08/24/89)

In article <2974@mace.cc.purdue.edu> ags@mace.cc.purdue.edu (Dave Seaman) writes:
>In an expression like "x*one(x)", it would be possible to skip the
>evaluation of the function "one" if x happens to be zero.  If "one" has
>side effects, then the result is implementation-dependent.  Assuming that
>both terms are evaluated, they may be taken in either order.  

Another example I just saw of a function with side effects causing problems
was in porting dvitty from Unix Pascal to Turbo.  At one point (in an out
of date copy) is a line something like

  do_something(getvalue,getvalue);

where getvalue is a function that reads a file to get a value.  On Unix, the
second getvalue was evaluated first, and the program worked; in Turbo, the
functions were evaluated from left to right, and the procedure ended up
with its parameters reversed.  Similar things could happen in any expression
that tried to read more than one value from a file.

Duncan Murdoch

leonard@bucket.UUCP (Leonard Erickson) (08/25/89)

edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) writes:

<   The following code produces 25 as output in Turbo Pascal 4.0.  Can
<anyone explain what is going on?
< 
<  var y:integer;

<  Function one(var x:integer):integer;
<  begin
<   x:=x+1;
<   one:=x;
<  end;
< begin
<  y:=4;
<  writeln(output,y*one(y));
< end.

by using "Var x:integer" instead of "x:integer" in the function header
you've told the computer to use the passed variable rather than a copy
of it. So when the function is executed it uses "y" everyplace that
it says "x". 

if you take out the "var" in the header, the program will output 20.
if you change the body from:
	x:=x+1;
	one:=x;
to:
	one:=x+1;
you'll get the same effect.

-- 
Leonard Erickson		...!tektronix!reed!percival!bucket!leonard
CIS: [70465,203]
"I'm all in favor of keeping dangerous weapons out of the hands of fools.
Let's start with typewriters." -- Solomon Short

abcscnuk@csuna.csun.edu (Naoto Kimura) (08/26/89)

As was stated a zillion times already:

Avoid using expressions where the order of evaulation may make a
difference.  Using functions with VAR parameters can be hazardous in
such cases, also those that change global variables (such behavior is
known as "side effects" as many people have noted).

Don't rely on the order of evaluation when it comes to parameters to a
function or procedure call, as this would be dependent upon the
implementation.   This becomes especially important if you want to
transport your code to a different computer or just a different
compiler.   This actually bit me hard when I tried to port a C program
(I know, I know, this isn't the newsgroup to mention that language, but
I haven't run across the same problem in pascal yet) when there was an
auto-increment of an index variable in one of the parameters.

Don't rely on the availability of short-circuit evaluation of booleans.
Nor should you rely on the availability of complete evaluation.  Try to
make your boolean expressions independent of such things.

And last, but least, there are some exceptions to these rules.

                //-n-\\			 Naoto Kimura
        _____---=======---_____		 (abcscnuk@csuna.csun.edu)
    ====____\   /.. ..\   /____====
  //         ---\__O__/---         \\	Enterprise... Surrender or we'll
  \_\                             /_/	send back your *&^$% tribbles !!

Brantly.WBST129@xerox.com (08/28/89)

Just to add on to Tom Wiencho's excellent response:

Re: "This is, of course, the difference between passing values by reference
(var) 
and passing them by value.  You generally do not want to pass by
reference if you plan on monkeying with the passed variables unless you 
want the side effects."

RE: ">>
>>   The following code produces 25 as output in Turbo Pascal 4.0.  Can
>>anyone explain what is going on?
>> 
>>  var y:integer;
>>
>>  Function one(var x:integer):integer;
>>  begin
>>   x:=x+1;
>>   one:=x;
>>  end;
>> begin
>>  y:=4;
>>  writeln(output,y*one(y));
>> end.

------

On pg. 67 of the Turbo Pascal User's Guide (5.0) it is stated:

"The keyword VAR in front of X and Y in GetData's procedure statement (as
shown on pg 66) says that the actual paramenters must be variables and that
the variable values can be changed and passed back to the caller."

In other words by using VAR you're explicitly telling the compiler that it
can do this [change the value of your variable]. This is NOT a weakness in
the Turbo compiler, but built-in flexibility for use by the programmer [
and ya know how programmers are, give us enough rope and we'll create a
bug! ]

Dennis

R&D  -Team Xerox-
Joseph C. Wilson Center for Technology
Webster, NY

edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) (08/29/89)

    When I originally posed the question about the evaluation
of  
    X:=4;
    WRITELN(OUTPUT,X*ONE(X));
 where
   FUNCTION ONE(VAR X:REAL):REAL;
   BEGIN
    X:=X+1;
    ONE:=X;
   END.

I was mildly surprised that result was 25 rather than 20, because
I thought that the evaluation of X might precede the evaluation of
ONE(X).  I should have more clearly stated that I was curious whether
the STANDARD had anything to say about the issue.  Some writers of
private letters have stated that the STANDARD says that the order of
evaluation is up to the implementor.
 
I encountered the question in the context of wanting to write a procedure
that had a side-effect on the variable X, and I was being cautious about
when I accessed X.  I wanted to be sure that I accessed X after it had
been changed.  I originally wrote  WRITELN(OUTPUT,ONE(X)*X) to be sure
that X was changed and then became curious whether my caution was warranted
in Turbo 4.0.  In practice, I would opt for the version in this paragraph,
which in most (all?) compilers would yield the desired result.  In any
case, I left behind a large caution sign about the trickery I was 
indulging in.  (Please note, that the actual code I wrote was not the
example I originally posted, but the example catches the spirit of the
actual code.)
 
   Edwin Kay
   CSEE
   Lehigh University

tom@stiatl.UUCP (Tom Wiencko) (08/29/89)

In article <616@lehi3b15.csee.Lehigh.EDU> edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) writes:
>> 
>>I encountered the question in the context of wanting to write a procedure
>>that had a side-effect on the variable X, and I was being cautious about
>>when I accessed X.  I wanted to be sure that I accessed X after it had
>>been changed.  I originally wrote  WRITELN(OUTPUT,ONE(X)*X) to be sure
>>that X was changed and then became curious whether my caution was warranted
>>in Turbo 4.0.  In practice, I would opt for the version in this paragraph,
>>which in most (all?) compilers would yield the desired result.  In any
>>case, I left behind a large caution sign about the trickery I was 
>>indulging in.  (Please note, that the actual code I wrote was not the
>>example I originally posted, but the example catches the spirit of the
>>actual code.)
>> 

Edwin:

Just a personal opinion here, and not wanting to start a flame war, but...

IMHO, this sort of code should be banned unless absolutely necessary.  The
potential for problems (due to new compilers, new versions of old compilers,
new people maintaining the code who do not read comments, and so on) far
outweigh restructuring your statements to allow function evaluations to happen
in an orderly fashion.  In compilers with really fancy optimization techniques,
all sorts of things not much related to the target expression might have an 
effect on how that expression is evaluated, and in cases like that you will
find yourself someday tracking down wierd problems.  I fixed one of those 
one day, where the addition or deletion of one line of code *100* lines away
from the problem area made a difference due to some pecularilarities of the
compiler.

Using side effects of function calls is a perfectly good programming technique.
Depending on the "average" behavior of a particular compiler or a particular
implementation of something not specified in the language is not a good 
technique.

Tom


-- 
Tom Wiencko                                            (w) (404) 977-4515
gatech!stiatl!tom                                  Wiencko & Associates, Inc.

usenet@cps3xx.UUCP (Usenet file owner) (08/30/89)

In article <616@lehi3b15.csee.Lehigh.EDU> edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) writes:
>
>    When I originally posed the question about the evaluation of: [ ...  ]
>
>I was mildly surprised that result was 25 rather than 20, because
>I thought that the evaluation of X might precede the evaluation of
>ONE(X).  I should have more clearly stated that I was curious whether
>the STANDARD had anything to say about the issue.

I don't know whether the standard explicitly says so (not having my copy
with me), but any program which depends on a particular order of
evaluation is erroneous--it isn't legal Pascal (though it may be
syntactically correct).  Ada explicitly distinguishes classes of errors
which the compiler MUST detect or MAY detect; dependence on order of
evaluation is an error, whether the compiler yells about it or not.

				Anton

+----------------------------------+------------------------+
| Anton Rang (grad student)	   | "UNIX: Just Say No!"   |
| Michigan State University	   | rang@cpswh.cps.msu.edu |
+----------------------------------+------------------------+

ags@mace.cc.purdue.edu (Dave Seaman) (08/30/89)

In article <4357@cps3xx.UUCP> rang@frith.egr.msu.edu (Anton Rang) writes:
>In article <616@lehi3b15.csee.Lehigh.EDU> edkay@lehi3b15.csee.Lehigh.EDU (Ed Kay) writes:
>>I was mildly surprised that result was 25 rather than 20, because
>>I thought that the evaluation of X might precede the evaluation of
>>ONE(X).  I should have more clearly stated that I was curious whether
>>the STANDARD had anything to say about the issue.

And I should have made it clearer when I posted my answer several days ago
that I was referring to the standard, which I had in front of me at the time.

I just assumed that when I said that "if the function 'one' has side
effects, then the result of x*one(x) is implementation-dependent", people
would know that I meant THE STANDARD SAYS that the result is
implementation-dependent.  What other basis could I possibly have had for
making the statement?  Certainly not by trying the program on different
compilers, which proves nothing.  The implementation could be wrong.

>I don't know whether the standard explicitly says so (not having my copy
>with me), but any program which depends on a particular order of
>evaluation is erroneous

Correct -- and the standard does explicitly say so, as I pointed out in my
previous posting.

-- 
Dave Seaman	  					
ags@seaman.cc.purdue.edu

ken@cs.rochester.edu (Ken Yap) (08/30/89)

|And I should have made it clearer when I posted my answer several days ago
|that I was referring to the standard, which I had in front of me at the time.
|
|I just assumed that when I said that "if the function 'one' has side
|effects, then the result of x*one(x) is implementation-dependent", people
|would know that I meant THE STANDARD SAYS that the result is
|implementation-dependent.  What other basis could I possibly have had for
|making the statement?  Certainly not by trying the program on different
|compilers, which proves nothing.  The implementation could be wrong.

Yes indeed. The standard is even more explicit than that.  The order of
evaluation of the operands of a binary operator is undefined and total
evaluation is not required in some cases. See J&W, 3rd Ed.