cspw.quagga@p0.f4.n494.z5.fidonet.org (cspw quagga) (06/10/90)
I've been coerced into writing a large application in Modula2 and my confusions and biases are coming to the fore. Can someone please comment on the following examples and tell me whether I win the prize for the most abstruse interpretation of the rules, or whether I have really understood the language properly. Problem: do something to every element in an array (say a string). PROCEDURE P(VAR S : ARRAY OF CHAR); VAR i,j : INTEGER; k,n : CARDINAL; BEGIN n := Length(S); FOR k := 0 TO n-1 DO process(S[k]); END; (* Wrong: For empty strings, n-1 is not a cardinal, or may be very big *) FOR i := 0 TO n-1 DO process(S[i]); END; (* Wrong: Can't mix INTEGER and CARDINAL in the same loop *) j := INTEGER(n); (* Lets assume this is okay *) FOR i := 0 TO j-1 DO (* The loop now goes correctly in all cases *) process(S[i]); END; (* Wrong: The index type of an open array is CARDINAL, so using an integer as a subscript is not strictly permitted. *) k := 0; WHILE (S[k] <> 0C) DO process(S[k]); INC(K); END; (* Wrong: Strings do not always store the trailing NUL byte. *) END P; Am I in a grumpy mood without justifiable cause? Pete -- uucp: uunet!m2xenix!puddle!5!494!4.0!cspw.quagga Internet: cspw.quagga@p0.f4.n494.z5.fidonet.org
Jim.Long@p15.f42.n105.z1.fidonet.org (Jim Long) (06/10/90)
In a message of <09 Jun 90 14:04:56>, cspw quagga (5:494/4) writes: > PROCEDURE P(VAR S : ARRAY OF CHAR); > VAR i,j : INTEGER; > k,n : CARDINAL; > BEGIN > n := Length(S); > k := 0; > WHILE (S[k] <> 0C) DO > process(S[k]); > INC(K); > END; > END P; WHILE k < n DO ... INC(k); ... -- uucp: uunet!m2xenix!puddle!42.15!Jim.Long Internet: Jim.Long@p15.f42.n105.z1.fidonet.org
Fred.van.der.Windt@f106.n512.z2.fidonet.org (Fred van.der.Windt) (06/12/90)
How about:
PROCEDURE P(VAR S : ARRAY OF CHAR);
VAR
i : CARDINAL;
BEGIN
(* this works: *)
IF S[0] <> CHR(0) THEN
FOR i := 0 TO Length(S) - 1 DO
process(S[i]);
END;
END;
(* this looks nicer: *)
FOR i := 1 TO Length(S) DO
process(S[i-1]);
END;
END P;
> Am I in a grumpy mood without justifiable cause?
I think so ;-)
Fred!
--
uucp: uunet!m2xenix!puddle!2!512!106!Fred.van.der.Windt
Internet: Fred.van.der.Windt@f106.n512.z2.fidonet.org
Peter.M..Perchansky@f101.n273.z1.fidonet.org (Peter M. Perchansky) (06/13/90)
Hello Peter: Why not do the following: PROCEDURE P (VAR s: ARRAY OF CHAR); VAR i, len : CARDINAL; BEGIN len := Length (s); FOR i := 0 TO len DO Process (s[i]) END; END P; 1) I am not sure why you subtracted one from the length in your example since Length should return the given values given the following examples: s := ''; (* empty string *) (* len will equal 0 *) s := 'Peter'; (* len will equal 5 *) 2) If you must subtract one, it is better to use the following: len := Length (s) - 1; instead of subtracting 1 from len in the FOR loop (some compilers might not optimize, and the expression will have to be evaluated every time. 3) To catch 0 - 1 errors when using CARDINAL, you can test it first. IF len # 0 THEN DEC (len) (* or len = len - 1 *) END; If you tell me what the procedure Process does, and what you are trying to accomplish, I (or some one else) might be better able to answer your questions. -- uucp: uunet!m2xenix!puddle!273!101!Peter.M..Perchansky Internet: Peter.M..Perchansky@f101.n273.z1.fidonet.org
ccc_ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) (06/13/90)
There are two interpretations of this situation: 1) Unless they're a natural fit to the problem you're trying to solve, stay away from zero-based arrays. Only C programmers could love them. 2) FOR-loops also have their problems. So who needs FOR, WHILE and REPEAT loops, anyway? Use a LOOP statement with one or more EXITs instead. Note that neither of these points is necessarily specific to Modula-2. Lawrence D'Oliveiro Computer Services Dept fone: +64-71-562-889 University of Waikato fax: +64-71-384-066 Hamilton, New Zealand electric mail: ldo@waikato.ac.nz DOS user interfaces have come a long way: Ctrl-K-B has given way to Alt-F4.
wolniewi@boulder.Colorado.EDU (WOLNIEWICZ RICHARD HANSON) (06/13/90)
In article <728.26767558@waikato.ac.nz> ccc_ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes: >There are two interpretations of this situation: > >1) Unless they're a natural fit to the problem you're trying to solve, > stay away from zero-based arrays. Only C programmers could love them. But look at what Modula 2 _requires_ us to use when we pass an array into a procedure. >2) FOR-loops also have their problems. So who needs FOR, WHILE and > REPEAT loops, anyway? Use a LOOP statement with one or more EXITs instead. Using FOR, WHILE, and REPEAT loops would usually seem to be preferable to LOOP statements with EXITs, for a number of reasons: 1) They more clearly express the intent of the programmer, and thus make the program easier to understand in the future. 2) Avoiding EXITs makes it easier (or trivial) to determine under what conditions the loop will terminate, making post-condition verification much easier. 3) Since FOR, WHILE, and REPEAT provide more semantic information, a compiler may be able to perform more optimization that in a LOOP construct. For example a compiler could check the constraints on the looping variable's range in a FOR loop at compile time, whereas using INC inside of the loop might require the code to check the constraints at run time, and on every iteration. Usage of looping constructs can be a very religious issue, of course. This is just my best understanding of the issues involved. . . ______ Laboratory for Atmpospheric and Space Physics | / \ (__ ._) University of Colorado, Boulder |__/ \____) | Richard Wolniewicz (wolniewi@tramp.colorado.edu) Disclaimer: Only the opinions of a grad student (me)
Jim.Long@p15.f42.n105.z1.fidonet.org (Jim Long) (06/14/90)
In a message of <12 Jun 90 14:17:02>, Peter M. Perchansky (1:273/101) writes: > Why not do the following: > > PROCEDURE P (VAR s: ARRAY OF CHAR); > VAR > i, len : CARDINAL; > BEGIN > len := Length (s); > FOR i := 0 TO len DO > Process (s[i]) > END; > END P; > > 1) I am not sure why you subtracted one from the length in your >example since Length should return the given values given the following >examples: > > s := ''; (* empty string *) > (* len will equal 0 *) And Process will process s[0], even though s[0] is indeterminate. > 2) If you must subtract one, it is better to use the following: > len := Length (s) - 1; instead of subtracting 1 from len > in the FOR loop It depends on what 'better' means. Some approaches take the time to save some memory space, some approaches take the space to save time. A WHILE will avoid the need for a decrement and solve the zero length problem at once: i := 0; WHILE i < Length( s ) DO Process( s[ i ] ); INC( i ); END; >some compilers might not optimize, and the expression will have to be >evaluated every time. Is it known that Length(s) is constant for the time duration of that loop? That is necessary before Length(s) can be optimized for use in testing the above WHILE condition. -- uucp: uunet!m2xenix!puddle!42.15!Jim.Long Internet: Jim.Long@p15.f42.n105.z1.fidonet.org
ccc_ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) (06/14/90)
In <22213@boulder.Colorado.EDU>, wolniewi@boulder.Colorado.EDU (WOLNIEWICZ RICHARD HANSON) points out (in answer to my suggestion about only using zero-based arrays where it makes sense to) that Modula-2 _requires_ open arrays to be zero-based. Agreed. In which case it may be better in some cases to avoid open arrays, and resort to a lower-level technique, on the grounds that this can make the code easier to understand. The lower-level technique I'm thinking of looks like this: TYPE ArbitraryArray = POINTER TO ARRAY [0 .. 32767] OF Whatever; PROCEDURE DoSomething ( WithArray : ArbitraryArray; LowBound, HighBound : CARDINAL ); VAR Index : CARDINAL; BEGIN FOR Index := LowBound TO HighBound DO DoSomethingWith(WithArray^[Index]) END (*FOR*) END (*DoSomething*); CONST ThisLowBound = 99; ThisHighBound = 243; VAR MyArray : ARRAY [ThisLowBound .. ThisHighBound] OF Whatever; BEGIN (*Mainline*) DoSomething ( ADDRESS(CARDINAL(ADR(MyArray)) - ThisLowBound * TSIZE(Whatever)), ThisLowBound, ThisHighBound ) END (*Mainline*). So long as LowBound is never passed as zero, you won't get "underflow" problems with CARDINAL. Yeah, sure it's yucky. But I submit that there may be sometimes when this is better than the alternative. Not always, just sometimes. Hanson goes on to give reasons for using FOR, WHILE and REPEAT instead of endless-LOOP-with-EXIT: "They more clearly express the intent of the programmer, and thus make the program easier to understand in the future." This is an unwarranted generalisation. Consider the following example (it should be pretty much self-explanatory): LOOP IF NrCharsLeft = 0 THEN StringsSame := TRUE; EXIT END (*IF*); INC(SourceIndex); DEC(NrCharsLeft); IF String1[SourceIndex] <> String2[SourceIndex] THEN StringsSame := FALSE; EXIT END (*IF*) END (*LOOP*) Do you think you could write it just as simply and clearly using something other than LOOP? I don't think so. Do you think this is an unusual example? I find that, in my programs, the majority of my loops are best written using LOOP-with-EXIT rather than WHILE, REPEAT or FOR. "Avoiding EXITs makes it easier (or trivial) to determine under what conditions the loop will terminate, making post-condition verification much easier." See above example again. The third point, about compiler optimisation--I admit Hanson has a point here. But if compilers were smarter, there would be less of a need for special-case constructs, so we can then avoid burdening the language (and the programmer) with them. Loops a religious issue? Now why would that be? In my days as a Computer Science student we used to argue about this sort of thing all the time. So long as the debate stays calm and reasonable, I'm happy to take part in it! Lawrence D'Oliveiro Computer Services Dept fone: +64-71-562-889 University of Waikato fax: +64-71-384-066 Hamilton, New Zealand electric mail: ldo@waikato.ac.nz New mistakes are the only kind worth making.
pseemann@inf.ethz.ch (Patrick Seemann) (06/14/90)
You may replace the FOR by a WHILE loop: n := Length (s); i := 0; WHILE (i < n) DO (* process s[i] *) INC (i); END (* WHILE *); greetings, pat
moeller@uniol.UUCP (Klaus Moeller) (06/14/90)
ccc_ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes: >This is an unwarranted generalisation. Consider the following example >(it should be pretty much self-explanatory): > LOOP > IF NrCharsLeft = 0 THEN > StringsSame := TRUE; > EXIT > END (*IF*); > INC(SourceIndex); > DEC(NrCharsLeft); > IF String1[SourceIndex] <> String2[SourceIndex] THEN > StringsSame := FALSE; > EXIT > END (*IF*) > END (*LOOP*) >Do you think you could write it just as simply and clearly using something >other than LOOP? I don't think so. Do you think this is an unusual example? >I find that, in my programs, the majority of my loops are best written >using LOOP-with-EXIT rather than WHILE, REPEAT or FOR. Of course no. But this was meant for loops with only one exit. You must use LOOP for loops that have more than one exit point simply because you can't do that with FOR, WHILE or REPEAT. But when you have only one exit point, WHILE, FOR and REPEAT are much clearer. In C you can have while and for loops with additional exits in them, Modula prohibits this. I suppose that Wirth wanted to isolate this in pure LOOPs. The Semantics of while, repeat and for become much easier this way. Klaus -- /---------------------------------------------------------------------\ / Klaus Moeller, Leiteweg 2, 2940 Wilhelmshaven, West - Germany \ | UUCP : moeller at uniol.uucp | BITNET : 078326 at DOLUNI1.BITNET | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| \ Fill what's empty, empty what's full and scratch where it itches / \---------------------------------------------------------------------/
wolniewi@boulder.Colorado.EDU (WOLNIEWICZ RICHARD HANSON) (06/15/90)
In article <747.2677cd57@waikato.ac.nz> ccc_ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes: >In <22213@boulder.Colorado.EDU>, wolniewi@boulder.Colorado.EDU >(WOLNIEWICZ RICHARD HANSON) points out (in answer to my suggestion >about only using zero-based arrays where it makes sense to) that >Modula-2 _requires_ open arrays to be zero-based. > >Agreed. In which case it may be better in some cases to avoid open >arrays, and resort to a lower-level technique, on the grounds that >this can make the code easier to understand. > >The lower-level technique I'm thinking of looks like this: [Code using pointer to array and array bounds passing] >Yeah, sure it's yucky. But I submit that there may be sometimes when this >is better than the alternative. Not always, just sometimes. I do agree that there are times that this technique would be useful (I frequently want to get around the array renumbering Modula 2 forces on us) but I would hesitate to use this in most situations, since it complicates the procedure interface and might discourage the next programmer on the project from re-using the code. In the specific instance of avoiding a CARDINAL value constraint, a simple IF statement around the loop would seem easier. [D'Oliveiro then disagrees that using LOOP constructs instead of FOR ... makes the code more difficult to understand, and offers the following code as an example.] > LOOP > IF NrCharsLeft = 0 THEN > StringsSame := TRUE; > EXIT > END (*IF*); > INC(SourceIndex); > DEC(NrCharsLeft); > IF String1[SourceIndex] <> String2[SourceIndex] THEN > StringsSame := FALSE; > EXIT > END (*IF*) > END (*LOOP*) > >Do you think you could write it just as simply and clearly using something >other than LOOP? I don't think so. I would probably write stringsSame := TRUE; WHILE stringsSame AND (index <= stringLength) DO stringsSame := (String1[index] = String2[index]); INC(index); END; (* WHILE *) Here I think it is just as clear, if not more so, that the intent of the programmer is to terminate the loop with stringsSame set correctly, and that the loop will terminate when a difference is found or the index exceeds the string length. Of course, this is where the opinionated nature of looping constructs comes in. >Loops a religious issue? Now why would that be? In my days as a Computer >Science student we used to argue about this sort of thing all the time. >So long as the debate stays calm and reasonable, I'm happy to take part in >it! I agree! That's why I have subscribed to this group, after all; to take part in discussions which will hopefully help us do this programming thing better. p.s. Hansen is my middle name, which the University recorded incorrectly. Please feel free to call me 'Richard', 'Wolniewicz' being somewhat cumbersome. . . ______ Laboratory for Atmpospheric and Space Physics | / \ (__ ._) University of Colorado, Boulder |__/ \____) | Richard Wolniewicz (wolniewi@tramp.colorado.edu) Disclaimer: Only the opinions of a grad student (me)
touch@dsl.cis.upenn.edu (Joe Touch) (06/15/90)
In article <22259@boulder.Colorado.EDU> wolniewi@tramp.Colorado.EDU (WOLNIEWICZ RICHARD H.) writes: > >I would probably write > > stringsSame := TRUE; > WHILE stringsSame AND (index <= stringLength) DO > stringsSame := (String1[index] = String2[index]); > INC(index); > END; (* WHILE *) > I'd prefer WHILE (index <= stringLength) AND (String1[index] = String2[index] DO INC(index); END; (* WHILE *) Unless my book is wrong, M2 is defined as having short-circuit evaluation of conjunctions and disjunctions. So if the index is out of bounds, the string element comparison never occurs, and the AND is safe. This omits the use of a redundant boolean (logical) variable, and not only is ir correct, but it will run faster (you don't really need to store the value of the comparison, do you?) I'd like to add that loops aren't awkward, but some PROGRAMMERS implementation of them IS. ;-) Joe Touch PS - A good text for a M2 course is "Modula-2" by Beidler and Jackowitz, PWS Publishers, 1986 (approx). It was developed by my advisor at the University of Scranton when I was there, where M2 has been used as a primary teaching language since 1984.
Jim.Long@p15.f42.n105.z1.fidonet.org (Jim Long) (06/15/90)
In a message of <14 Jun 90 17:07:42>, cspw quagga (5:494/4) writes: >There have been a number of suggestions that my awkward problem >of processing every element in a string should be coded >like this > i := 0; > WHILE (i < Length(s)) DO > process(S[i]); > INC(i); > END; > >Although this works nicely, it violates all the ideas that one >should distinguish between definite loops (where the number of iterations >is known in advance, and a FOR loop is the most appropriate >construct), and an indefinite loop where one iterates until or while >the conditions have not been fulfilled. Without seeing the definition of 'process(CHAR)' we cannot determine from this code fragment which type of loop we have. If S[i] is passed as a VAR CHAR, the loop may be indefinite. -- uucp: uunet!m2xenix!puddle!42.15!Jim.Long Internet: Jim.Long@p15.f42.n105.z1.fidonet.org
cspw.quagga@p0.f4.n494.z5.fidonet.org (cspw quagga) (06/15/90)
There have been a number of suggestions that my awkward problem of processing every element in a string should be coded like this i := 0; WHILE (i < Length(s)) DO process(S[i]); INC(i); END; Although this works nicely, it violates all the ideas that one should distinguish between definite loops (where the number of iterations is known in advance, and a FOR loop is the most appropriate construct), and an indefinite loop where one iterates until or while the conditions have not been fulfilled. So although this loop satifies the problem, imho it is stylistically bad. Pete -- EP Wentworth - Dept. of Computer Science - Rhodes University - Grahamstown. Internet: cspw.quagga@f4.n494.z5.fidonet.org Uninet: cspw@quagga uucp: ..uunet!m2xenix!quagga!cspw -- uucp: uunet!m2xenix!puddle!5!494!4.0!cspw.quagga Internet: cspw.quagga@p0.f4.n494.z5.fidonet.org
Cameron.Barnard@f715.n153.z1.fidonet.org (Cameron Barnard) (06/15/90)
I've read several of the answers people have given you and they are good but I'm not sure that they were what you were looking for. It is difficult to tell exactly what you are trying to do from the small segments of code you gave as examples but here is some general advice. If you have ever programmed in BASIC or any other unstructured language you will do best to forget everything you have learned there. Modula-2 is a highly structured language that is extremely powerful but can be tricky to use because YOU HAVE TO HAVE A DEFINITE PLAN OF ACTION BEFORE YOU EVEN BEGIN TO START PROGRAMMING. This has advantages and disadvantages. You have a lot of power but it takes a while to get the hang of it. I used to program in BASIC and found Modula-2 quite difficult to learn but now that I am familar I will NEVER go back to basic. (Unless I want to write something really quick and dirty.) I highly reccomend a book by Rick Sutcliffe titled INTRODUCTION TO PROGRAMMING USING MODULA-2. It is published by Merrill and is an excellent and very readable book. Good Luck and hang in there! Cameron Barnard. -- uucp: uunet!m2xenix!puddle!153!715!Cameron.Barnard Internet: Cameron.Barnard@f715.n153.z1.fidonet.org
wolniewi@boulder.Colorado.EDU (WOLNIEWICZ RICHARD HANSON) (06/15/90)
In article <26032@netnews.upenn.edu> touch@dsl.cis.upenn.edu (Joe Touch) writes: >I'd prefer > > WHILE (index <= stringLength) AND (String1[index] = String2[index] DO > INC(index); > END; (* WHILE *) > >Unless my book is wrong, M2 is defined as having short-circuit evaluation >of conjunctions and disjunctions. So if the index is out of bounds, >the string element comparison never occurs, and the AND is safe. This is something I had never known before. I assumed short-circuit evaluation was in use, but I also (apparently incorrectly) assumed that the compiler could evaluate the expressions in whatever order it found to be optimal. Could someone find the reference for this? If this is true, I look forward to taking advantage of it. >This omits the use of a redundant boolean (logical) variable, >and not only is ir correct, but it will run faster (you don't >really need to store the value of the comparison, do you?) I think you do need that comparison result (remember my example was based upon another netter's previous example). Why compare two strings if you aren't going to use the comparison result? . . ______ Laboratory for Atmpospheric and Space Physics | / \ (__ ._) University of Colorado, Boulder |__/ \____) | Richard Wolniewicz (wolniewi@tramp.colorado.edu) Disclaimer: Only the opinions of a grad student (me)
beers@acsu.Buffalo.EDU (Andrew Beers) (06/16/90)
M2 IS defined as having short circuit evaluation of expressions, but it is the implementer's choice whether or not to have the compiler use it. Not all features of every language are implemented in every compiler. :( Andrew Beers ------------ beers@autarch.acsu.buffalo.edu Suny @ Buffalo Buffalo, NY
jem@cs.hut.fi (Johan Myreen) (06/20/90)
In article <22422@boulder.Colorado.EDU> wolniewi@boulder.Colorado.EDU (Richard Wolniewicz) writes: >(not clearly stated in my last posting) is this; does the Modula 2 language >insure that a short circuit evaluation of an expression will evaluate the >different components in order of appearance in the code. For example, in the >code below, >IF A() OR B THEN ... >where A is a function call returning a BOOLEAN, and B is a BOOLEAN variable, >does Modula 2 insure that A will be called, or could the compiler optimize Yes. Also, if A() is TRUE, B will not be evaluated at all. The order is left to right, and as soon as the result is known evaluation stops. Someone said in an earlier message that this was not the case in some compilers, and that it is the implementer's choice whether the compiler supports this feature or not. This is not true. If a compiler doesn't implement short circuit evaluation, it isn't a Modula-2 compiler. -- Johan Myreen jem@cs.hut.fi
Peter.M..Perchansky@f101.n273.z1.fidonet.org (Peter M. Perchansky) (06/26/90)
Hello Jim: In a msg dated 23 Jun 90 08:09:06, Jim Long said: **********************BEGIN QUOTE***************************** In an article of <21 Jun 90 15:40:03 GMT>, lins@Apple.COM (Chuck Lins) writes: > Compilers are allowed to strip dead code. Constant boolean > expressions may never need to be evaluated at run-time at all. I haven't tested my Modula-2 system in this regard, but I recall years ago that UCSD p-System's Pascal compiler treated BOOLEAN constants in IFs like conditional compilation. If the constant was FALSE, the compiler would not generate code for the THEN clause. Does anyone know of any M2 compilers with this feature? ***********************END QUOTE****************************** Although I don't know of any compilers having this feature, I do know of one that comes close. TopSpeed Modula-2 2.00 has compiler pragmas for conditional compilation. A very short example follows: CONST DEBUG = TRUE; ... BEGIN ... (*%T DEBUG *) (* DEBUG STATEMENTS *) (* FOLLOW HERE *) ... (*%E *) ... END; -- uucp: uunet!m2xenix!puddle!273!101!Peter.M..Perchansky Internet: Peter.M..Perchansky@f101.n273.z1.fidonet.org
borchert@MATHEMATIK.UNI-ULM.DE (Andreas Borchert) (06/27/90)
Jim Long writes: > In an article of <21 Jun 90 15:40:03 GMT>, lins@Apple.COM (Chuck Lins) writes: > > > Compilers are allowed to strip dead code. Constant boolean > > expressions may never need to be evaluated at run-time at all. > > I haven't tested my Modula-2 system in this regard, but I recall years ago > that UCSD p-System's Pascal compiler treated BOOLEAN constants in IFs like > conditional compilation. If the constant was FALSE, the compiler would not > generate code for the THEN clause. Does anyone know of any M2 compilers with > this feature? At least all Modula-2 compilers which descend from the Lilith Modula-2 compiler have this feature. Andreas Borchert -- borchert@mathematik.uni-ulm.de borchert@dulruu51.bitnet