Почему Паскаль запрещает модификацию счетчика внутри блока for? - PullRequest
7 голосов
/ 15 января 2010

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

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

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

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

Ответы [ 7 ]

13 голосов
/ 20 января 2010

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

  • цикл WHILE может никогда не завершиться (выражение может быть просто ИСТИНА)
  • конечное число раз выполнения цикла FOR должно быть известно до того, как он начнет выполняться. Вы должны знать, что циклы FOR всегда заканчиваются.

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

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

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

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

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

Еще одно такое свойство, кстати, заключается в том, что переменная цикла не определена послецикл for.Это также облегчает оптимизацию

11 голосов
/ 20 января 2010

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

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

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

Семейство Control Data Corporation 6600, в состав которого входит Cyber, представляет собой архитектуру RISC, использующую 60-битные слова центральной памяти, на которые ссылаются 18-битные адреса. Некоторые модели имели (дорогой, поэтому необычный) вариант, модуль сравнения-перемещения (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 бит; Х регистров 60 бит. Настройка от А1 до А5 имеет побочный эффект загрузки соответствующего регистра X1-Х5 содержимым загруженного адреса. Настройка A6 или A7 записывает соответствующее содержимое X6 или X7 в адрес, загруженный в регистр A. A0 и X0 не связаны. Регистры B могут использоваться практически в каждой инструкции в качестве значения для добавления или вычитания из любого другого регистра A, B или X. Следовательно, они отлично подходят для небольших прилавков.

Для эффективного кода регистр B используется для переменных цикла, поскольку для них могут использоваться инструкции прямого сравнения (B2 <100 и т. Д.); сравнения с регистрами X ограничены отношениями с нулем, поэтому сравнение регистра X с 100, скажем, требует вычитания 100 и проверки результата на значение меньше нуля и т. д. Если было разрешено присвоение переменной цикла, 60-битное значение перед назначением в регистр B должен быть проверен диапазон. Это настоящая проблема. Герр Вирт, вероятно, полагал, что и хлопоты, и неэффективность не стоили утилиты - в такой ситуации программист всегда может использовать цикл <code>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 символов, пропуская символ новой строки и что-то еще.
  • оценка полного выражения (по сравнению с ярлыками). Они были реализованы не путем прыжка и установки одного или нуля (как это делают большинство генераторов кода сегодня), а с помощью инструкций процессора, реализующих булеву арифметику.
7 голосов
/ 17 января 2010

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

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

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

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

(Примечание: Delphi, однако, имеет оператор Break, а также Continue и Exit, что походит на return в C.)

Очевидно, нам не нужно , чтобы иметь возможность изменять управляющую переменную в цикле for, потому что мы всегда можем переписать, используя цикл while. Пример в C, где используется такое поведение, можно найти в разделе 7.3 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;
    }
}
3 голосов
/ 16 января 2010

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

1 голос
/ 17 января 2010

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

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

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

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

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

Привет

Марка

1 голос
/ 17 января 2010

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

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

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

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

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

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

1 голос
/ 15 января 2010

С Для цикла

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

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

...