И.Г. Головин - Конспект лекций по курсу Языки программирования (1161120), страница 17
Текст из файла (страница 17)
Сейчас считается, что эту свободу нужно ограничить.Примеры свободы в старых языках программирования.ENTRY M-- альтернативный вход в процедуруSUBROUTINE P(X, Y, Z, *, *, *)Некоторые параметры могли быть звездочками.Формальные параметры * могли соответствовать процедурам.RETURN *i98Здесь i либо 1, либо 2P(1,2,-1,56,128)Глядя на P, нельзя сказать, где окажешься после вызова.Это полностью противоречит правилам структурного программирования, поэтому от этойвозможности сейчас полностью отказались.Процедура: один вход, один выход.И.Г.: Единственное категоричное правило: не следовать категоричным правилам.Правило: не делать процедуру длиннее 50, 25, …, N строк является слишком категоричным.Желательно, чтобы процедура отражала законченный алгоритм.
Не нужно менятьструктуру программы в угоду формальным требованиям. Когда не следовать правилам, акогда следовать – подскажет здравый смысл.Проанализируем подробнее передачу управления. На диаграмме вначале начинает работуглавная программа, затем она вызывает подпрограмму (вызов 1), подпрограмма выполняетработу и завершается, передавая управление главной подпрограмме (возврат 1). Затемглавная программа работает еще какое-то время, после чего вновь вызывает подпрограмму(вызов 2), подпрограмма выполняет работу и завершается, передавая управление главнойподпрограмме (возврат 2).Caller(главная программа)Callee(подпрограмма)Вызов 1ВызовподпрограммыВозврат 1Вызов 2ВызовподпрограммыВозврат 2Видна несимметричность главной программы и подпрограммы.
Как создатьсимметричность?В начале 60-х годов выдвинули концепцию сопрограммы. Сопрограммы передаютуправление друг другу поочередно.99Сопрограмма 1Сопрограмма 2Передача 1Передачауправлениясопрограмме 2Передачауправлениясопрограмме 2Передача 2Передачауправлениясопрограмме 1Передача 3Передача 4Передачауправлениясопрограмме 1Сопрограмма называется subroutine.CALL – вызов подпрограммы.TRANSFER P (RESUME P) – возобновление сопрограммы.Некоторые процессы с использованием сопрограмм описываются проще.1я статья: Дейкстра.2я статья: Хоар.3я: Далл Ньюберг.Страуструп (датчанин) придумал C++.
В этой 3ей статье были описаны сопрограммы, сотсылкой к языку Simula67, и приведен пример задачи, которая очень просто решается припомощи сопрограмм.Задача: дано 2 упорядоченных файла. Необходимо составить третий файл, объединяющийте два, и тоже упорядоченный.Решение: пусть есть буфер. Вначале инициализируется большим значением.
Каждаяподпрограмма пишет в новый файл до тех пор, пока текущее значение не больше значениябуфера. Потом она пишет следующее значение в буфер и передает управление.Другой пример: лексический и синтаксический анализ. Они могут поочередно передаватьдруг другу управление, при этом схема работы программы будет выглядеть проще.Из языков высокого уровня, поддерживающих сопрограммы, наиболее известен языкModula 2.PROCEDURE NEW_PROCESS(P: PROC; VAR C: ADDRESS; N: CARDINAL);…TRANSFER(VAR cor1, cor2: ADDRESS);…100Возникает вопрос: в чем смысл третьего параметра процедуры NEW_PROCESS?Каждая подпрограмма создает свой фрейм (frame), которой есть запись активации(activation record). Он включает в себя:формальные параметры,адрес возврата,локальные переменные.Запись активации располагается в стеке.В архитектуре mainframe IBM 360-370 стека не было.Именно поэтому адрес возврата хранится в записи активации в соответствующей области.Для сопрограмм записи активации должны храниться в специальном списке (ни в коемслучае не на стеке, так как иначе они уничтожатся).N – это размер записи активации.
Про него ничего сказать нельзя.Понятие сопрограммы – очень хорошее и красивое понятие, но при его использованиипоявляются низкоуровневые проблемы.Это одна из причин того, что концепция сопрограмм не использовалась в ЯП.Сейчас эта парадигма возрождается.Вирт использовал сопрограммы для квазипараллельного программирования.Механизм мониторов был реализован в Модула2 на языковом уровне.Пример из книги Вирта:SEND(VAR S:SIGNAL)WAIT(VAR S:SIGNAL)Если рассмотреть любое API работы с потоками, то можно найти много общего.В чем разница сопрограмм и потоков? В потоках нет явного RESUME. Планировщик берет насебя роль выделения того процесса, который готов.Пример использования сопрограмм:IOTRANSFER – указание передать управление некому драйверу – обработчику прерываний.Параметром задается номер прерывания.Таким образом, при помощи сопрограмм была реализована концепция драйвера.В Оберон в таком виде это уже не вошло.
Хотя сама концепция программы весьма красива.Передача параметров в подпрограммуP(arg1, …, argN) – описание подпрограммыP(e1, … , eN) – вызовСуществует два способа передачи параметров в функцию: позиционный и ключевой.101Позиционный способ передачи параметров: нумеруются в том порядке, в какомперечислены. Формi = Фактi (при этом имена параметров при описании не важны).В Python реализован другой способ – ключевой способ: по имени. В языке Ада:имя: exprОбъявление процедурного типаTYPE Prc = PROCEDURE(VAR:INTEGER;REAL);X: PrcX:= PX(I,1.0)С точки зрения реализации, такие типы суть указатели.Пусть дана функция void f (int = 0);Фактически, это две функции.void f(int i) {…}void f() {f(0);}Две такие записи эквивалентны. Точно так же X(int i = 0); (конструктор).В C# появились параметры по умолчанию.Чем удобен ключевой способ? Можно перечислить все параметры, кроме параметров поумолчанию.Cпособы передачи параметров.Под «способом» может подразумеваться 2 вещи:1. Механизм передачи (как именно осуществляется передача)А вообще хорошо или плохо когда начинаем обсуждать механизм?Смотря с какой точки зрения! (Например, языки программирования – хорошо илиплохо? Asm хорошо или плохо? Смотря с какой точки зрения!)2.
Inout – семантика (семантика изменяемости)Не по механизму, а по семантике передачи входных параметров.В языке Ада хотели рассмотреть параметры со 2 точи зрения:Список имен: спецификатор имя_типаX,Y: out T;A: in T1;…Может ли изменяться параметр?Спецификатор – in,out, inout.Формальный параметр / фактический параметр: как меняется формальный параметр именяется ли в соответствии с ним фактический?102В АДА от in требуется только неизменяемое значение (out наоборот).
inout – требуется истатус неопределенности и изменяется (может и использоваться и изменяться).Компилятор выберет механизм передачи сам. Если не стоит спецификатор, то по умолчаниюв АДА in. А механизм передачи уж определит компилятор.На первый взгляд идея здравая, если не анализировать последствия (отвечала духу языкаАДА – многое перекладывать на компилятор). В итоге: к чему пришли создатели языка АДА?Обычно под способом имеется ввиду первое – конкретный механизм передачи (в языквходит низкоуровневость).Вообще говоря, способов 5(если взять все языки)1.2.3.4.5.по значению,по результату,по значению/результату,по адресу (ссылке),по имени.Рассмотрим эти способыОпишем семантику первых трех способов (семантика копирования – выделяется память подформальный параметр, то есть идёт копирование туда и/или обратно)Как производится соответствие формальных и фактических параметров?В современных компьютерных архитектурах аппаратно реализуемый стек – для передачипараметров.
В подпрограммах образуют стек, следовательно, место в стеке выделяется подформальный параметр.По значению: копирование формального параметра в фактический (фактический в стек),вызов процедуры и всё (отсутствует обратное копирование). Даже если фактическийпараметр меняется, то всё равно, т.к. отсутствует обратное копирование, фактический неменяется.
В данном случае реализована in-семантика. Изменение формального параметра неприводит к изменению фактического. Передача по значению чревата последствиями, еслипараметры велики.По результату: реализована out-семантика. Производится только обратное копированиеперед return.По значению/результату: и то, и другое. Этот вариант удобен с точки зренияфункциональности, но совершенно неудобен с точки зрения удобства.В Pascal можно передавать параметры по значению и по ссылке.Советуем массивы передавать как параметр-переменную, чтобы минимизироватькопирование массива.В случае большого фактического параметра – передавать его по ссылке (теряяin-семантику).По ссылке: вместо значения фактического параметра копируется его адрес.103Ссылка не то же самое, что адрес! Содержит ли ссылка адрес? В общем случае да.
Сборщикмусора перезагружает адрес, но ссылка продолжает работать.Существует ЯП, в котором есть единственный способ передачи параметров – наболееуниверсальный способ – по адресу(первые версии Бэйсика,Фортрана).Язык С имеет единственный способ передачи по значению – он не обеспечивает in-out(outможно реализовать путем явной передачей адреса). Есть операция взятия адреса любогообъекта. Вообще, если есть понятие указателя, а также возможность получения адресалюбого объекта, то можно передать параметр по адресу ссылки.Передачу параметров по значению нужно рассматривать для совместимости с системнымивызовами.COM API – на C++, макросы на языке СПоэтому передача по значению, хотя бы для того чтобы была совместимость с языком С.Чтобы ответить на вопрос об эффективности неизбежно спускаемся на нижний уровень.К чему в итоге пришли большинство ЯП? Как производится современная передачапараметров?Специфицируется механизм.Вспомним про язык АДА.Для массивов передача только по адресу, но при этом следит за in и out семантикой.На первый взгляд здорово, чего же не хватило? Почему в АДА 95 создатели сказали «простойТД int будем передавать поз значению, а остальные по адресу (или ссылке)»?Пришли к тому же способу что и сейчас.С++ по значению, по ссылке 1 ,4.Стандартный Паскаль 1,4 .Java 1: если хотите менять значение переменной, но семантики нет, упаковывай в объект ипередавай по ссылкеПростые ТД – значения копируются, а для сложных – копирование ссылок (то есть как бы поссылке).4 способ для объектовОбъекты не именуются – именуются ссылки.Передача по ссылке за бесплатно.Пример (C#)В С# два способа: по значению 1, явным образом 4 способ ref(outсемантика)104void f(X t){t = //не оказывает влияния на фактический параметр}void f(ref X t){t = // фактический параметр меняется}X a = new X();Ref aRef требует определённости объекта, а out не требуетF(ref a/out a)Если неинициализированная ссылка, то возникнет прерывание.