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.