为什么Pascal禁止修改for block中的计数器?

Jic*_*hao 7 pascal for-loop loop-counter

是因为Pascal被设计成是这样,还是有任何权衡?

或者禁止或禁止修改for-block中的计数器有什么优缺点?恕我直言,没有什么用于修改for-block内的计数器.

编辑:
你能提供一个我们需要修改for-block内的计数器的例子吗?

这是很难 wallyk答案,并cartoonfox的答案之间作出选择,因为两者的答案是如此nice.Cartoonfox分析,从语言方面的问题,同时wallyk分析,从历史和现实世界aspect.Anyway的问题,所有的答案的感谢我要特别感谢wallyk.

Daf*_*ees 14

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

  • a WHILE loop may never terminate (the expression could just be TRUE)
  • the finite number of times a FOR loop is to execute is supposed to be known before it starts executing. You're supposed to know that FOR loops always terminate.

The FOR loop present in C doesn't technically count as a FOR loop because you don't necessarily know how many times the loop will iterate before executing it. (i.e. you can hack the loop counter to run forever)

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

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

In strictly theoretical terms, you shouldn't ever need to modify the counter within a for loop. If you could get away with it, you'd just have an alternative syntax for a WHILE loop.

You can find out more about "while loop computability" and "for loop computability" in these CS lecture notes: http://www-compsci.swan.ac.uk/~csjvt/JVTTeaching/TPL.html

Another such property btw is that the loopvariable is undefined after the for loop. This also makes optimization easier


wal*_*lyk 12

Pascal首次用于CDC Cyber​​-a 1960年代和1970年代的大型机 - 与今天的许多CPU一样,具有出色的顺序指令执行性能,但对分支机构也有显着的性能损失.网络架构的这个和其他特征可能严重影响了Pascal的for循环设计.

简短回答是,让一个循环变量赋值将需要额外的保护代码和搞砸优化其能正常地在18位索引寄存器以及处理循环变量.在那些日子里,由于硬件的费用和无法以任何其他方式加速,软件性能受到高度重视.

答案很长

Control Data Corporation 6600系列包括Cyber​​,是一种RISC架构,使用由18位地址引用的60位中央存储器字.有些型号有一个(昂贵的,因此不常见的)选项,比较移动单元(CMU),用于直接寻址6位字符字段,但是不支持任何类型的"字节".由于CMU一般不能计算在内,因此大多数Cyber​​代码都是因为缺少而生成的.每个单词十个字符是通常的数据格式,直到支持小写字符让位于暂定的12位字符表示.

指令是15位或30位长,除了CMU指令实际上是60位长.因此,每个字中最多包含4个指令,或者两个30位,或者一对15位和一个30位.30位指令不能跨越字.由于分支目的地可能仅引用单词,因此跳转目标是字对齐的.

该架构没有堆栈.实际上,过程调用指令RJ本质上是不可重入的. RJ通过在RJ指令所在的位置写入跳转到下一条指令来修改被调用过程的第一个字.被叫程序通过跳转到它们的开头返回给调用者,这是为返回链接保留的.程序从第二个字开始.为了实现递归,大多数编译器都使用了辅助函数.

寄存器文件有八个实例,每个寄存器有三种,A0..A7用于地址操作,B0..B7用于索引,X0..X7用于通用算术.A和B寄存器是18位; X寄存器是60位.设置A1到A5的副作用是将相应的X1到X5寄存器加载到加载的地址的内容中.设置A6或A7将相应的X6或X7内容写入加载到A寄存器的地址.A0和X0未连接.B寄存器几乎可以在每个指令中用作从任何其他A,B或X寄存器中加或减的值.因此它们非常适合小型计数器.

对于有效的代码,B寄存器用于循环变量,因为可以在它们上使用直接比较指令(B2 <100等); 与X寄存器的比较仅限于零关系,因此将X寄存器与100进行比较,例如,需要减去100并测试结果小于零等.如果允许对循环变量赋值,则为60位值在分配给B寄存器之前必须进行范围检查.这真是一件麻烦事.维尔特先生可能猜到的是,麻烦和低效是不值得的效用-程序员可以随时使用whilerepeat... until循环在这种情况下.

额外的怪异

几种独特的Pascal语言功能直接与Cyber​​的各个方面相关:

  • pack关键字:一个单一的"性格"消耗了60位字,或者是挤满每个字十个字符.
  • (不寻常)alfa类型:packed array [1..10] of char
  • 内在程序pack()unpack()处理打包字符.它们不对现代架构进行转换,只进行类型转换.
  • text文件的古怪与file of char
  • 没有明确的换行符.记录管理已明确调用writeln
  • 虽然set of char在CDC上非常有用,但由于其过多的内存使用(8位ASCII的32字节变量/常量),它在许多后续的8位机器上都不受支持.相比之下,单个Cyber​​词可以通过省略换行符和其他内容来管理本机62个字符集.
  • 完整表达评估(与快捷方式相对).这些不是通过跳转和设置一个或零(如今大多数代码生成器所做的那样)实现的,而是通过使用实现布尔算术的CPU指令实现的.


P-N*_*uts 7

Pascal最初被设计为一种鼓励块结构编程的教学语言.Kernighan(K&K的K)撰写了一篇关于Pascal限制的文章(可理解的偏见),为什么Pascal不是我最喜欢的编程语言.

关于修改什么帕斯卡尔调用禁止控制变量 a的for循环,再加上缺乏一个联合break声明中表示,这是不可能知道的循环体有多少次,不研究它的内容执行.

没有break语句,并且在循环终止后无法使用控制变量更多的是限制而不是无法修改循环内的控制变量,因为它阻止了一些字符串和数组处理算法被写入"明显的" " 办法.

Pascal和C之间的这些和其他区别反映了它们最初设计的不同哲学:Pascal强制执行"正确"设计的概念,C允许或多或少的任何东西,无论多么危险.

(注:德尔福确实有Break但是声明,以及ContinueExit这就像return在C.)

显然,我们永远不需要能够在for循环中修改控制变量,因为我们总是可以使用while循环重写.使用此类行为的C中的示例可以在K&R第7.3节中找到,其中printf()引入了简单版本.处理'%'格式字符串中的序列的代码fmt是:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然这使用指针作为循环变量,但它同样可以用字符串中的整数索引写入:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,但是 Turbo Pascal 支持 `break` 作为非标准语言扩展:它不是标准 Pascal (ISO 7185) 的一部分。我在 Delphi 的上下文中提到了扩展,因为 Turbo Pascal 现在仅具有历史意义(它可能是我使用过的第一种编译语言)。 (2认同)
  • 当实际上 Delphi 就是 Turbo Pascal 时,将 Turbo Pascal 视为仅具有“历史意义”而忽略它有点不公平(-:自从我在 1983 年得到我的(第一个)副本以来,已经发生了很多演变和增长,但本质上它已经成长了(通过将对象(呃,不久前)添加到另一个中。 (2认同)