2010-01-15 7 views
7

Это потому, что Паскаль был спроектирован так, или есть какие-то компромиссы?Почему Паскаль запрещает модификацию счетчика внутри блока?

Или какие плюсы и минусы запрещают или не запрещают модификацию счетчика внутри блока? IMHO, мало использовать для изменения счетчика внутри блока.

EDIT:
Не могли бы вы предоставить один пример, в котором мы должны изменить счетчик внутри для-блока?

Это трудно выбирать между ответом wallyk и ответом cartoonfox, так как и ответ настолько анализ nice.Cartoonfox проблема с языкового аспекта, в то время как wallyk анализа проблема из истории и реального мира aspect.Anyway, спасибо за все ваши ответы, и я хотел бы выразить особую благодарность wallyk.

ответ

13

В программировании теории языка (и в теории вычислимости) WHILE и FOR петлями имеют различные теоретические свойства:

  • цикл WHILE может никогда не прекращать (выражение может быть только TRUE)
  • Конечное число раз, когда цикл FOR равен выполнение должно быть известно до начала его выполнения. Вы должны знать, что цикл FOR всегда заканчивается.

для цикла присутствует в C технически не считается для цикла, потому что вы не обязательно знать, сколько раз цикл будет перебирать перед его выполнением. (т. е. вы можете взломать счетчик циклов для запуска навсегда)

Класс проблем, которые вы можете решить с помощью циклов WHILE, является более мощным, чем те, которые вы могли бы решить с помощью строгого цикла цикла, найденного в Pascal.

Pascal спроектирован таким образом, чтобы учащиеся имели две различные конструкции цикла с различными вычислительными свойствами. (Если вы внедрили FOR C-way, цикл FOR будет просто альтернативным синтаксисом для while ...)

В строго теоретических терминах вам никогда не придется изменять счетчик в цикле for. Если вам это удастся, у вас будет просто альтернативный синтаксис для цикла WHILE.

Вы можете узнать больше о «в то время как петля вычислимости» и «цикл вычислимости» в этих конспектов CS: http://www-compsci.swan.ac.uk/~csjvt/JVTTeaching/TPL.html

Другой такое свойство кстати, что loopvariable не определен после того, как цикл. Это также упрощает оптимизацию

+0

Это лучший ответ до сих пор. – MaD70

+1

«Оператор for используется, когда число итераций известно заранее» - Wirth 1973, язык программирования Pascal (Пересмотренный отчет) http://maben.homeip.net/static/S100/software/pascal/1973%20The%20Programming%20Language%20Pascal.pdf –

1

От For loop

В некоторых языках (не C или C++) переменная петля неизменен в пределах объема тела цикла, с любой попытки изменить его значение будучи рассматриваются как семантические ошибка. Такие модификации иногда являются следствием ошибки программиста, , которая может быть очень сложной для идентифицировать один раз. Однако только изменений, скорее всего, будут обнаружены компилятором . Ситуации, в которых адрес переменной передается в качестве аргумента подпрограмме, сделать это очень сложно проверить, так как поведение подпрограммы в общем случае непознаваемо для компилятора.

Так что это, похоже, поможет вам не сжечь вашу руку позже.

+0

Самая большая проблема, с которой я сталкиваюсь, заключается в том, что по крайней мере, когда я делал Pascal/Delphi, это * не было * задокументировано в справке. Как ни странно, я думаю, что он работал в Turbo Pascal 6, но больше не на Delphi. – Joey

+0

Да, loopvar не является неизменным в Delphi (использование его делает его более дорогостоящим). Loopvar все еще не определено после afaik –

3

Он может сделать некоторые оптимизации (например, для разворачивания цикла): нет необходимости в сложном статическом анализе, чтобы определить, предвидится ли поведение цикла или нет.

1

Отказ от ответственности: Прошло десять лет с тех пор, как я последний раз занимался PASCAL, поэтому мой синтаксис может быть не совсем точным.

Вы должны помнить, что PASCAL является ребенком Nicklaus Wirth, и Вирт очень сильно заботился о надежности и понятности, когда он проектировал PASCAL (и все его преемники).

Рассмотрим следующий фрагмент кода:

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

Не глядя на процедуру FOO, ответить на следующие вопросы: Есть ли когда-нибудь закончится этот цикл? Откуда вы знаете? Сколько раз процедура FOO вызывается в цикле? Откуда вы знаете?

PASCAL запрещает изменять индексную переменную в теле цикла, чтобы ВОЗМОЖНО знать ответы на эти вопросы и знать, что ответы не изменятся, когда и если будет изменена процедура FOO.

+0

Конечно, передача семантики Pascal не гарантирует, что тело цикла for прекратится ... –

7

Паскаль был первоначально разработан как учебный язык для поощрения блочно-структурированного программирования.Керниган (K of K & R) написал (понятно смещенное) эссе об ограничениях Паскаля, Why Pascal is Not My Favorite Programming Language.

Запрет на изменения, что Паскаль называет переменной управления из for петли, в сочетании с отсутствием break заявления означает, что можно узнать, сколько раз тело цикла выполняется без изучения его содержимого.

Без оператора break и невозможность использования управляющей переменной после окончания цикла является скорее ограничением, чем невозможностью модифицировать управляющую переменную внутри цикла, поскольку она предотвращает использование некоторых алгоритмов обработки строк и массивов написанных на «очевидном» пути.

Эти и другие различия между Pascal и C отражают различные философии, с которыми они были впервые разработаны: Паскаль, чтобы обеспечить концепцию «правильного» дизайна, C, чтобы разрешить больше или меньше всего, независимо от того, насколько опасно.

(Примечание: Delphi действительно есть Break заявление, однако, а также Continue и Exit которая подобна return в ° С)

Ясно, что мы никогда не необходимости чтобы быть в состоянии изменить переменную управления в for, потому что мы всегда можем переписать цикл while. Пример в C, где такое поведение используется, можно найти в разделе K & R, где представлена ​​простая версия 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; 
    } 
} 

Хотя это использует указатель в качестве переменного цикла, оно в равной степени было написано с целочисленным индексом в строку:

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; 
    } 
} 
+0

«Turbo pascal» поддерживает 'break' с давних времен. – Jichao

+1

Да, но Turbo Pascal поддерживает 'break' как нестандартное языковое расширение: он не является частью стандартного Pascal (ISO 7185). Я упомянул расширения в контексте Delphi, поскольку Turbo Pascal теперь представляет только исторический интерес (возможно, это был первый скомпилированный язык, который я когда-либо использовал). –

+1

Да. Я имел в виду «Turbo pascal» только для того, чтобы продемонстрировать, что «Почему Pascal - это не мой любимый язык программирования» как-то обременен. – Jichao

1

Возможно, безопасно заключить, что Pascal был разработан для предотвращения изменения индекса цикла for внутри цикла. Стоит отметить, что Pascal ни в коем случае не единственный язык, который мешает программистам делать это, другой пример - Fortran.

Есть две веские причины для проектирования языка, таким образом:

  1. программы, в частности, для петель в них, легче понять и, следовательно, легче писать и модифицировать и проверить.
  2. Петли легче оптимизировать, если компилятор знает, что счетчик поездки через цикл устанавливается перед входом в цикл и после этого инвариантен.

Для многих алгоритмов это поведение является необходимым поведением; например, обновление всех элементов в массиве. Если память обслуживает Pascal, также предусмотрены циклы do-while и петли с повторением до конца. Большинство, я полагаю, алгоритмов, которые реализованы в языках C-стиля с модификациями индексной переменной цикла или вырываются из цикла, могут быть легко реализованы с помощью этих альтернативных форм цикла.

Я поцарапал себе голову и не нашел убедительной причины для разрешения модификации переменной индекса цикла внутри цикла, но тогда я всегда считал, что это делает плохую конструкцию, и выбор правильной петли построить как элемент хорошего дизайна.

С уважением

Марк

11

Pascal был впервые реализован для CDC Cyber-1960-х и 1970-х годов мейнфрейма, который сегодня, как и многие процессоры, имеет отличную последовательную производительность выполнения команд, но также значительно снижает производительность для филиалов. Эти и другие характеристики кибер-архитектуры, вероятно, сильно повлияли на дизайн Паскаля в петлях for.

Короткие ответы заключается в том, что разрешение присвоения переменной цикла потребует дополнительного защитного кода и перепутанной оптимизации для переменных цикла, которые обычно можно обрабатывать в 18-разрядных индексных регистрах. В те дни производительность программного обеспечения была высоко оценена из-за расхода оборудования и невозможности ускорить его каким-либо другим способом.

Длинный ответ

Контроль Корпорация Data 6600 семья, которая включает в себя Cyber, является архитектура RISC с использованием 60-разрядных слов центральной памяти, на которые ссылается 18-битовые адреса. Некоторые модели имели (дорогостоящий, поэтому необычный) вариант, Compare-Move Unit (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. Это настоящая хлопот. Г-н Вирт, вероятно, полагал, что и хлопот, и неэффективность не стоили полезности - программист всегда может использовать цикл while или repeat ... until в этой ситуации.

Дополнительная странность

несколько уникальных к Pascal особенности языка непосредственно связаны с аспектами Cyber:

  • pack ключевое слово: либо один «символ» потребляет 60-разрядное слово , или он упакован десять символов на слово.
  • (необычный) Тип alfa: packed array [1..10] of char
  • внутренние процедуры pack() и unpack() иметь дело с упакованными символов. Они не выполняют преобразование на современных архитектурах, а только преобразование типов.
  • странность text файлов против file of char
  • нет явного символа новой строки. Управление записью было явно вызвано с помощью writeln
  • Хотя set of char был очень полезен на CDC, он не поддерживался на многих последующих 8-разрядных машинах из-за избыточного использования памяти (32-байтовые переменные/константы для 8-разрядного ASCII). Напротив, одно Cyber-слово могло бы управлять нативным набором из 62 символов, опуская новую строку и что-то еще.
  • полная оценка выражения (по сравнению с ярлыками).Они были реализованы не путем прыжка и установки одного или нуля (как это делают большинство генераторов кода), а с помощью инструкций CPU, реализующих логическую арифметику.
+0

+1 для аспект истории. – Jichao

+1

Это вы Mel? – ergosys

+0

Спасибо, но Мел дошел до меня на несколько лет. (Трудно было не описать ППУ-периферийные процессоры). – wallyk