Why does Pascal forbid counter modification inside the block?

Is it because Pascal was designed that way, or are there any compromises?

Or what pros and cons prohibit or do not prohibit counter modification inside the block? IMHO, it’s not enough to use to change the counter inside the block.

EDIT :
Could you give one example where we need to change the counter inside the block?

it's tough to choose between wallyk and cartoonfox answer since both answers are so nice. Cartoonfox analyzes the problem in terms of language, and wallyk analyzes the problem from the history and real world aspect.Anyway, thanks for all your answers, and I would like to express special thanks to wallyk.

+7
for-loop pascal
source share
7 answers

In programming language theory (and computability theory), WHILE and FOR loops have different theoretical properties :

  • WHILE loop can never end (expression can only be TRUE)
  • it is assumed that a finite number of FOR loops should be known before it can be executed. You should know that FOR loops always end.

The FOR loop present in C is technically not considered a FOR loop because you do not necessarily know how many times the loop will iterate before it executes. (i.e. you can crack the loop counter to execute forever)

The class of problems you can solve with WHILE loops is more powerful than the ones you could solve with the strict FOR loop found in Pascal.

Pascal is designed so that students have two different cycle designs with different computational properties . (If you implemented the FOR C-way, the FOR loop would be just an alternative while syntax ...)

In strictly theoretical terms, you never have to change the counter in a for loop. If you succeed, you just get an alternative syntax for the WHILE loop.

You can learn more about the “computational ability of the loop” and “to calculate the loops” in these CS lecture notes: http://www-compsci.swan.ac.uk/~csjvt/JVTTeaching/TPL.html p>

Another such property of btw is that loopvariable is undefined after the for loop. It also simplifies optimization.

+13
source share

Pascal was first implemented for the Cyber-a CDC mainframe of the 1960s and 1970s, which, like many modern processors, had excellent consistent command execution performance, but also a significant decrease in performance for branches. This and other characteristics of cyber architecture have probably greatly influenced the design of Pascal for loops.

The short answer is that assigning a loop variable will require additional security code and confused optimizations for loop variables that can usually be processed in 18-bit index registers. In those days, software performance was highly rated due to hardware costs and the inability to speed it up in any other way.

Long answer

The Control Data Corporation 6600 family, which includes Cyber, is a RISC architecture using 60-bit central memory words referenced by 18-bit addresses. Some models had a (expensive, therefore unusual) option, Compare-Move Unit (CMU), for direct access to 6-bit character fields, but otherwise there was no support for "bytes" of any type. Since CMU could not be calculated in general, most of the Cyber ​​code was created for its absence. Ten characters in one word was the usual data format until support for lowercase characters was replaced by a preliminary 12-bit character representation.

The instructions are 15 bits or 30 bits in length, except that CMU instructions are 60 bits long. Thus, up to 4 instructions packed into each word, or two 30 bits, or a pair of 15 bits and one 30 bits. 30-bit instructions cannot span words. Since branch assignments can only refer to words, transition goals are word aligned.

There is no stack in architecture. In fact, the RJ procedure call statement is intrinsically inherently. RJ changes the first word of the called procedure, writing down the transition to the next command after where the RJ instruction is. The called procedures return to the caller, jumping to their beginning, which is reserved for feedback. Procedures begin with the second word. Most compilers used a helper function to implement recursion.

The register file has eight instances of each of the three types of registers, A0..A7 for address processing, B0..B7 for indexing, and X0..X7 for general arithmetic. Registers A and B - 18 bits; X registers - 60 bits. Setting A1-A5 has the side effect of loading the corresponding register X1-X5 with the contents of the loaded address. Setting A6 or A7 writes the corresponding contents of X6 or X7 to the address loaded into register A. A0 and X0 are not connected. Registers B can be used in almost every instruction as a value to add or subtract from any other register A, B or X. Therefore, they are great for small counters.

For effective code, register B is used for loop variables, since they can use direct comparison instructions (B2 <100, etc.); Comparison with X-registers is limited to zero, so comparing an X-register by 100, say, requires subtracting 100 and testing the result less than zero, etc. If you allowed the assignment of a loop variable, the 60-bit value must be checked by the range before assigning to register B. This is a real hassle. Mr. Wirth probably believed that the hassle and inefficiency were not worth the utility - a programmer can always use a while or repeat ... until in this situation.

Extra weirdness

Several unique features of the one-on-Pascal language relate directly to aspects of Cyber:

  • pack keyword: either one “character” consumes a 60-bit word, or ten characters per word are packed.
  • (unusual) alfa type: packed array [1..10] of char
  • internal procedures pack() and unpack() for handling packed characters. They do not perform conversion on modern architectures, but only type conversion.
  • weird text files vs file of char
  • no newline character. Record management was explicitly called using writeln
  • While set of char was very useful for CDC, it was not supported on many subsequent 8-bit machines due to excessive memory usage (32-byte variables / constants for 8-bit ASCII). On the contrary, a single Cyber-word could control the native set of 62 characters, omitting a new line and something else.
  • full evaluation of the expression (compared to labels). They were implemented not by jumping and setting one or zero (as most code generators do), but using CPU instructions that implement logical arithmetic.
+11
source share

Pascal was originally developed as a learning language to encourage block-structured programming. Kernighan (KK & R) wrote a (understandably biased) essay on Pascal's limitations, Why Pascal is not my favorite programming language .

Preventing Pascal from calling the control variable for loop, combined with the absence of a break statement, means that you can find out how many times the loop body is executed without examining its contents.

Without a break statement, the inability to use a control variable after the end of the loop is more of a limitation than the inability to modify the control variable inside the loop, since it prevents some algorithms from processing strings and arrays from being written in an “obvious” way.

These and other differences between Pascal and C reflect the different philosophies with which they were first developed: Pascal to provide the concept of the “right” design, C to allow more or less of anything, no matter how dangerous.

(Note: Delphi has a break statement, as well as Continue and Exit , which is similar to return in C.)

It’s clear that we never need to be able to change the control variable in a for loop, because we can always rewrite it with a while . An example in C where this behavior is used can be found in section 7 of K & R, where a simple version of printf() is introduced. Code that processes the sequences '%' in an fmt format string:

 for (p = fmt; *p; p++) { if (*p != '%') { putchar(*p); continue; } switch (*++p) { case 'd': /* handle integers */ break; case 'f': /* handle floats */ break; case 's': /* handle strings */ break; default: putchar(*p); break; } } 

Although this uses a pointer as a loop variable, it could also be written with an integer index into a string:

 for (i = 0; i < strlen(fmt); i++) { if (fmt[i] != '%') { putchar(fmt[i]); continue; } switch (fmt[++i]) { case 'd': /* handle integers */ break; case 'f': /* handle floats */ break; case 's': /* handle strings */ break; default: putchar(fmt[i]); break; } } 
+7
source share

It can simplify some optimizations (for example, for unrolling loops): there is no need for complex static analysis to determine if the behavior of the loop is predictable or not.

+3
source share

From To cycle

In some languages ​​(not C or C ++), the loop variable is constant inside the body volume of the loop, with any attempt to change its value it is considered as a semantic error. such modifications are sometimes the result of a programmer error, which can be very difficult to identify once. However, only openly changes are likely to be detected by the compiler. Situations in which the address of the loop variable as an argument to the routine make it very difficult to verify, because routine behavior is generally unrecognizable to the compiler.

So it looks like it will help you not burn your hand later.

+1
source share

Disclaimer: It has been several decades since I last worked on PASCAL, so my syntax may not be entirely accurate.

You must remember that PASCAL is a child of Nicklaus Wirth, and Wirth was very concerned about reliability and comprehensibility when he designed PASCAL (and all of his successors).

Consider the following code snippet:

 FOR I := 1 TO 42 (* THE UNIVERSAL ANSWER *) DO FOO(I); 

Without looking at the FOO procedure, answer these questions: does this cycle end? How do you know? How many times is the FOO procedure called in a loop? How do you know?

PASCAL prohibits the modification of the index variable in the body of the loop, so it is POSSIBLE to know the answers to these questions and to know that the answers will not change when and if the FOO procedure is changed.

+1
source share

It is probably safe to conclude that Pascal was designed to prevent the for loop index from changing inside the loop. It is worth noting that Pascal is by no means the only language that prevents programmers from doing this, another example is Fortran.

There are two good reasons to design a language this way:

  • Programs, in particular for loops in them, are easier to understand and therefore easier to write and modify and verify.
  • Loops are easier to optimize if the compiler knows that the trip counter through the loop is set before entering the loop and is then invariant.

For many algorithms, this behavior is the desired behavior; for example, updating all elements in an array. If the memory serves Pascal, do-while loops and loops with repeating to the end are also provided. Most, I think, algorithms that are implemented in C-style languages ​​with changes in the loop index variable or break out of the loop can be easily implemented using these alternative loop forms.

I scratched my head and did not find a convincing reason for allowing the change of the variable loop index in the loop, but then I always thought that it was a bad design and choosing the right loop design as an element of good design.

Hi

Mark

+1
source share

All Articles