лекции (2008) (Фингеров Александр_ Кононов Алексей_ Кузин Сергей) (1160833), страница 9
Текст из файла (страница 9)
InOut.writeString(“count =”);
InOut.writeInt(i);
InOut.writeln;
В Java и C# реализовано все достаточно просто. Они опираются на то, что все объекты имели общего объекта предка – класс Object. Таким, образом список переменной длины- массив объектов типа object.
Void f(params int[] integers) {};
Все параметры должны быть integer, либо приводимы к нему.
Write(string format, params Object[] objs);
Обеспечивается надежность во время выполнения.
Надо различать следующие функции:
Void f(params int[] integers); //список переменных произвольной длины
Void f(int[] integers); //массив
Похожая проблема была в языке Java, причем создатели не хотели расширять язык, вводя новые ключевые слова. Они синтаксически расширили язык, не вводя ключевые слова.
Int f (int … integers)
{
Int r=0;
For (int I : integers)
R+= I;
return I;
} // с integers работаем как с массивом
Пункт 3. Подпрограммные типы данных.
- передача п/п как параметров в других п/п.
TYPE PROCINT = PROCEDURE (INTEGER);
VAR P:PROCINT;
TYPE F = PROCEDURE (REAL): REAL;
PP = PROCEDURE (VAR T);
Определены операции присваивания: V:= const;
P (5);
В C/C++ имеется механизм указателей на функцию, в Ада-95 есть как подпрограммные тип , так и подпрограммные переменные.
-
Наследование.
-
Композиция.
-
Референция.
-
Делегирование (некий объект отдает нам один метод, который мы можем вызывать).
В Оберон было наследование. Предлагалось реализовать наследование, используя обработчик (handler).
Лекция 18
п.3. Подпрограммные типы данных.
Реализация подпрограммных (далее п/п) типов данных:
Через указатели - CALL P.
C, C++, Pascal, Modula-2, Oberon(2), Ada 95.
Modula-2,Oberon,Delphi: type PRC:=procedure (integer);
:= и () – 2 операции.
Заметим, что в Аде 83 не было понятия п/п типа, а в Аде 95 – появился. В Аде 83 передавать адреса переменных не было никакой необходимости, компилятор сам решал что же нужно сделать. Такой же тип данных может быть записан на Ада как type PRC is access procedure PRC(i: is integer).
Вызов:
procedure FCALL(P:PRC) is
begin
P(0);
end FCALL;
Появляется специальная разновидность формальных параметров, которые явно передаются по ссылке.
Особняком отдельно стоят 2 языка – С++ и Дельфи. Та же семантика, но они выделяются понятием класса, и поэтому отдельно существуют старые понятия процедуры и функции, а дополнительно – понятие член класса или метод класса. Передаётся неявный параметр – в С++ this, в Дельфи self.
void Foo(int);
typedef void (*Footype)(int);
Footype f; f=Foo; (*f)(0); f(0);
Это явное определение функционального типа.
class Bar{
void Foo(int);
int i;
};
почему бы и тут не сделать указатель на член класса?
Int Bar::*pb;
Bar b;
pb=&b.i;
typedef void (Bar::*)Foo (int);
Foom f;
f=Bar::foo;
операции .* и ->*
C# и Java.
Java отличается принципиально тем, что понятие функционального типа в нём отсутствует, так как это совершенно ненадёжная вещь. Нельзя передавать функции как параметры, но можно это реализовать. Пример необходимости передачи функций как параметров – пример с вычислением интеграла f(x)dx. На других языках мы описали бы функциональный тип данных, объявили бы функцию интеграл. В Java, чтобы написать это, необходимо использовать понятие класса.
class Integral{
public double IF(double){..}
public double Integrate(double a, double b, double EPS){..}
};
//теперь нам нужно просто унаследоваться от класса, чтобы добавить подынтегральную функцию
В С# сделали существенный шаг вперед. Здесь есть понятие делегата. Фактически, делегат – специализированный функциональный тип данных, который тем не менее не сводится к указателю на функцию.
public delegate void Operation(int);
Делегату можно присваивать новое значения, мы имеем право объявлять переменные делегатского типа.
public Operation dlgt;
class Bar{
public void Foo(int x){..}
public static void Foo2(int i){..}
};
Bar b;
dlgt=new Operation(b.Foo);
dlgt(0); //b.Foo(0);
dlgt=new Operation(Bar.Foo2);
dlgt(0); //Bar.Foo2(0);
Нам совершенно наплевать, что там – функция или статическая функция. Для делегатских операций определены операции += и -=. Делегат – по сути цепочка, изначально инициализируется пустым значением.
Можно написать:
dlgt += new Operation(b.Foo);
dlgt += new Operation(Bar.Foo);
Более того, объект класс можно порождать внутри скобок.
Все классы delegate происходят от класса System Delegate, где есть методы static Delegate Combine и static Delegate Remove. Есть функция, которая возвращает массив делегатов GetInvocationList. Вызов delegate от 1 это по сути синтаксическое сокращения.
Понятие события.
subscribe(distribute) – подписка на событие и уведомление при его вызове. Фактически, мы подписываемся на делегат. Пример обратного вызова – таймер, или почтовая система. При этом существует EventSource – ему принадлежит переменная делегатского типа и EventConsumer – подписчик, он использует операции += и -=. В С# - многие операции и структуры – «синтаксический сахар». Event – частный случай экземпляра делегата, это не тип данных. Частность заключается в ограничении операций. Делегат не используется как тип данных, так как термин инкапсуляция должен распространяться на все экземпляры одного типа.
Дополнительно в С# появились анонимные делегаты.
public delegate int Operation2(int x,int y);
Operation2 op=delegate(int x,int y){return x+y;}
Это простейший случай. Представим ещё один.
int i;
delegate(int x,int y){return x+y+i;}
// в качестве контекста могут включаться даже локальные переменные, это называется «захват»
Анонимный делегат может захватывать не только локальные переменные, но и например свой контекст – класс. Есть возможность реализации связывания. Создаётся класс, внутри которого есть делегатское поле и целое значение.
class Binder{
public Binder(Operation2 d, int x){del=d,arg=x};
private Operation2 del;
private int arg;
public Operation1 GetBind2(){return delegate(int a){return del(a,arg);}
};
Binder B=new Binder(Mult,1);
B.GetBind2()(10);
Лекция 19.
Глава 5. Логические модули в традиционных ЯП. Определение новых ТД с помощью ЛМ. Инкапсуляция и АТД.
п.1 Понятие Логического Модуля.
Фортран, Алгол 60, стандартный Паскаль.
Основная цель Логического Модуля — различно взаимосвязанные ресурсы объединить в один, чтобы показать эту связь.
Типы Данных — множество операций + множество значений. Главное здесь — множество операций. Цель логического модуля в частности была связать структуру данных и множество операций, то есть определить тип данных.
Понятие класса — грубо говоря мягкая эволюция понятия структуры (записи). Теперь класс является одновременно действительно взаимосвязанным набором ресурсов — он является по определению ТД, а с другой стороны — и логическим модулем. Модульная составляющая класса проявляется, но основная его задача — определение новых ТД.
п.2 Логические модули в языках Модула-2, Оберон, Дельфи.
М-2:
-
Главный модуль.
-
Библиотечный модуль.
-
Локальный модуль.
MODULE name;
объявления
BEGIN
оп-ры
END name.
(это главный модуль в Модуле-2. Если поменять MODULE на PROGRAM, получим Дельфи)
Библиотечные модули:
-
Модуль определений.
-
Модуль реализации.
DEFINITION MODULE M;
объявления;
END M;
IMPLEMENTATION MODULE M;
объявления и реализация
END M.
.DEF -объекты данных.
.MOD -типы данных.
Интерфейс -заголовки процедур и функций
DEFINITION MODULE stacks;
CONST N=256;
TYPE stack=
RECORD
TOP: INTEGER;
BODY:ARRAY[0..N] of REAL;
END;
PROCEDURE INIT(VAR S:stack);
PROCEDURE POP(VAR S:stack):REAL;
...
VAR Done:BOOLEAN; (~errno)
END stacks.
DM
=> unit (unit stacks;
interface
uses (список имен)
...
implementation
...
end stacks.)
IM
БМ => (export)
<= (import)
Модель Видимости — имя либо видимо, либо невидимо. Видимость бывает двух типов: потенциальная видимость, это означает , что имя может быть видимо, но только с каким-то уточнением ( имя_модуля.идентификатор) и непосредственная видимость. В модели Модулы-2 непосредственно видимы только имена модулей.
IMPORT список_имен_БМ; (делает потенциально видимыми все имена, которые объявлены)
VAR S:stacks.stack;
FROM stacks IMPORT stack,pop
В Модуле-2 нет перегрузки имён, все имена должны быть уникальными. В случае если наше имя конфликтует с глобальным именем, наше имя имеет приоритет. Эта проблема во внешних библиотеках решается добавлением уникального префикса в идентификаторах.
Модули делятся на сегменты экспортируемых и невидимых имён.
Язык Оберон ещё более упростил эту схему. Вирт вообще отказался от понятий главного и локального модуля. Предполагается, что программа на языке Оберон погружается в консольную систему, есть специальная команда загрузки модуля М и пользователь может выбрать любую процедуру. Экспортируемые процедуры без параметров в ОС Оберон называется командой. Понятие функции main как таковое отсутствует.
LOAD M
M.P
В 1988 году появился Оберон 2. В самом первом Обероне было 2 модуля — DEFINITION (правила точно такие же как в языке Модула-2) и MODULE (просто модуль). Программы на языке Оберон не могут быть написаны в отрыве от ОС Оберон, поэтому модуль DEFINITION может быть сгенерирован «главным модулем», и остался только MODULE. Экспортируемые имена помечаются символом * после идентификатора. *- ставится когда переменная экспортируется только на чтение. В языке Оберон осталась единственная форма импорта — только в потенциальную видимость.
MODULE M;
определения
END M;