Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Новости и прочее

Заметки о выходе из функции без значения и зеркальности get и put

Выход из функции без значения

            Язык PL/1 имеет недостаток в слабом различии подпрограмм и функций (в терминах языка это процедуры и процедуры-функции). Процедура-функция должна возвращать значение через оператор RETURN(значение), а просто процедура может иметь выход через RETURN без параметра. При этом в процедуре необязательно всегда ставить явный RETURN, достаточно просто «добраться» до последнего оператора этой процедуры.

            Но такой подход приводит к тому, что и в процедуре-функции можно добраться до последнего оператора, не используя RETURN, и, тем самым, выйти без значения. Конечно, я вставил в компилятор проверку, что тело процедуры-функции содержит хотя бы один оператор RETURNS, и если это не так — в последней строке текста процедуры-функции выдается ошибка «БЕЗ ОТВЕТА». Но, разумеется, это неполная проверка, например, правильно работающая процедура функция F может оканчиваться так:
. . .
IF A>B THEN RETURN(A); ELSE RETURN(B);
END F;
 а неправильная — так:
. . .
IF A>B THEN RETURN(A); // ELSE RETURN(B);
END F;
            И тут пришло в голову использовать для проверки место в компиляторе, где выбрасываются команды, на которые невозможно передать управление. Реализовано это очень просто: метки, как написанные программистом, так и расставленные самим компилятором имеют такой же внутренний формат, как и обычные уже сгенерированные команды процессора. Когда оптимизатор анализирует связанный список сформированных команд и находит команды безусловного перехода или возврата, он проверяет, что следующая «команда» — обязательно внешняя или внутренняя метка. Если это не метка — все команды до ближайшей метки выбрасываются. Я чуть улучшил исходный вариант этого места в компиляторе разбором, кто поставил эти команды без метки: компилятор или программист? Если сам компилятор — то команды выбрасываются без сообщения, иначе лучше сообщить программисту о таком странном месте, возможно это ошибка.

            Так вот, при разборе процедуры-функции в конце её тела компилятор теперь принудительно ставит некоторую фиктивную команду. Эта команда не превращается в коды процессора. Её единственное назначение — быть отброшенной как команда, на которую невозможно передать управление. Если же последующие части оптимизатора обнаружили такую команду, значит формально на неё можно передать управление и, следовательно, формально можно добраться до конца процедуры-функции, избежав операторов RETURN. Тогда текст:
IF A>B THEN RETURN(A); // ELSE RETURN(B);
END F;
даст предупреждение:
ВОЗМОЖЕН ВЫХОД БЕЗ ЗНАЧЕНИЯ ИЗ ФУНКЦИИ F

О зеркальности get и put

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

            Лет 30 объяснял всем (новичкам снисходительно), что операторы get/put в PL/1 зеркальны друг другу и это было сделано специально при проектировании языка.

            Мы часто пользовались этой зеркальностью. Например, одна программа печатает переменные в текстовый файл, другая их читает. Список переменных через запятую хранится в отдельном файле и вставляется в обе программы директивой %include.

            Т.е. оператор вывода выглядит как-нибудь так:
put file(f) data(%include ‘c:\test\param.txt’);
Удобно менять этот список только в одном месте.

            Однако вчера вдруг выяснилось, что для форматируемого ввода-вывода зеркальности нет. Если s — это, например, строка из 3 символов, то put edit(s)(a); печатает 3 символа, а якобы зеркальный оператор get edit(s)(a); сработает по-другому. Сначала из файла прочитается текстовый элемент по формату «а» (вся строка), а затем первые три символа перепишутся в переменную s. Если далее идут ещё элементы, они потеряются.

            Возник вопрос: кто виноват в потере зеркальности? Ошибка реализации, ошибка проектирования или ошибка моего восприятия? Cклоняюсь, что это ошибка моего восприятия. При проектировании, похоже, никто и не заморачивался никакой зеркальностью. Это я вообразил себе, что при проектировании была такая цель. Правда, дальше начинается схоластика: а то, что никто не заморачивался зеркальностью — это ошибка проектирования или нет?

            Зеркальность ввода-вывода удобна для изучения и легка для восприятия, но формально в языке не определена и, как выяснилось, не везде соблюдается.

            Ну, и лишний раз подтвердилась мудрость: век живи — век учись. Считал себя знатоком всех тонкостей и через 30 (!) лет узнал, что неправильно понимал свой рабочий инструмент.

            P.S. У Чехова, кажется, был рассказик, про человека, который годами ходил по улице мимо лавки с вывеской "большой выбор сигов" и удивлялся, чем одна рыба сиг отличается от другой. Через 10 лет он, наконец, прочитал вывеску. там было написано "большой выбор сигар"

Автор: Д.Ю.Караваев. 12.02.2019

Опубликовано: 2019.02.14, последняя правка: 2019.02.14    00:35

Оцените

Отзывы

     2019/02/14 00:06, Автор сайта          # 

Зеркальность, одинаковость подходов дают возможность упирать на логику, а не рассчитывать на память. Почему бы, к примеру, не прийти к единому правилу, как должны располагаться играющие разные роли аргументы функций? Допустим, справа — источники информации, слева — приёмники. Или наоборот, но главное — чтобы везде одинаково. Это убережёт от множества ошибок. Зачем тут разнобой?
  fputc( символ_откуда, файл_куда);             // --> слева направо
fgets( строка_куда, сколько, файл_откуда); // <-- справа налево
(См. «Слева направо или справа налево?»). Тема зеркальности правильная: не столь важно, какое движение, правостороннее или левостороннее, главное, что имеет место быть только одно из них.

О возврате и невозврате значений. Когда в языке есть синтаксически обозначенные 1) чистые функции, 2) недетеминированные без побочных эффектов, 3) с побочными эффектами, 4) недетеминированные с побочными эффектами, то это помогает разобраться с возвратом значений.

Если у нас функция, возвращающее значение, имеет 1-й или 2-й тип, то в них и слово «return» не нужно. Возвращаемым значением является последнее выражение:
    . . .	// тело функции 1-го или 2-го типа
0) // последняя строка функции, здесь подразумевается return 0
Если у нас процедура, то интересные моменты тоже возможны. Процедура — это подпрограмма, не возвращающая значений, смысл её существования — в производстве побочного эффекта.

Допустим, f(x) — функция производящая побочный эффект, а g(y) и h(z) — его не производящие.
    . . .	// тело процедуры (3-й или 4-й тип)
f(x) // производим побочный эффект
g(y) // не производим побочный эффект
h(z) // не производим побочный эффект
return)
Вызов функций без побочного эффекта между f(x) и «return» (а это функции g(y) и h(z)) не имеет никакого смысла. Наработанные этими функциями результаты не используются далее для производства побочного эффекта. Т.е. это либо ошибка, либо выдача предупреждения о «лишнем коде», либо молчаливая оптимизация, этот код просто игнорирующая.

     2019/02/16 14:52, kt          # 

Еще по поводу "зеркальности". Справедливости ради: в стандарте языка изначально не было оператора типа get list(s)(a); А операторы обмена строки с фиксированной длиной типа get list(s)(a(3));/ put list(s)(a(3)); остаются зеркальными друг другу. Так что, возможно, изначально зеркальность в проекте языка все-таки предполагалась.

Добавить свой отзыв

Написать автору можно на электронную почту mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Каким должен быть язык программирования?

Анализ и критика

Описание языка

Компилятор

Отечественные разработки

Cтатьи на компьютерные темы

Двадцать тысяч строк кода, которые потрясут мир?

Почему владение/заимствование в Rust такое сложное?

Масштабируемые архитектуры программ

Почему Хаскелл так мало используется в отрасли?

Бесплатный софт в мышеловке

Исповедь правового нигилиста

Русской операционной системой должна стать ReactOS

Почему обречён язык Форт

Программирование без программистов — это медицина без врачей

Электроника без электронщиков

Программисты-профессионалы и программирующие инженеры

Статьи Дмитрия Караваева

●  Идеальный транслятор

●  В защиту PL/1

●  К вопросу о совершенствовании языка программирования

●  О реализации метода оптимизации при компиляции

●  О реализации метода распределения регистров при компиляции

●  О распределении памяти при выполнении теста Кнута

●  Опыты со стеком или «чемпионат по выполнению теста Кнута»

●  О размещении переменных в стеке

●  Сколько проходов должно быть у транслятора?

●  Чтение лексем

●  Экстракоды при синтезе программ

●  Об исключенных командах или за что «списали» инструкцию INTO?

●  Типы в инженерных задачах

●  Непрерывное компилирование

●  Об одной реализации специализированных операторов ввода-вывода

●  Особенности реализации структурной обработки исключений в Win64

●  О русском языке в программировании

●  Формула расчета точности для умножения

●  Права доступа к переменным

●  Заметки о выходе из функции без значения и зеркальности get и put

●  Ошибка при отсутствии выполняемых действий

●  Скорость в попугаях

●  Крах операции «Инкогнито»

●  Предопределённый результат

Компьютерный юмор

Новости и прочее

Последние комментарии

2019/06/25 11:22 ••• VIT1972
Идеальный транслятор

2019/06/05 23:21 ••• kt
К вопросу о совершенствовании языка программирования

2019/06/04 14:52 ••• Неслучайный читатель
Деньги = работа / знание

2019/05/29 00:42 ••• rst256
Программирование без программистов — это медицина без врачей

2019/05/14 16:10 ••• utkin
Обработка ошибок

2019/05/09 18:05 ••• евгений
Русский язык и программирование

2019/04/28 14:08 ••• Автор сайта
Статьи Дмитрия Караваева

2019/04/22 16:19 ••• Автор сайта
Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

2019/04/03 22:24 ••• Антон
Все голосования

2019/04/02 12:28 ••• Автор сайта
Шестнадцатиричные и двоичные константы

2019/04/02 12:25 ••• Автор сайта
Выбор кодировки для компилятора

2019/03/24 14:55 ••• Автор сайта
Реализация двухстековой модели размещения данных