лекции (2015) (1160856), страница 7
Текст из файла (страница 7)
This class// implements IEnumerable so that it can be used// with ForEach syntax.public class People : IEnumerable27{private Person[] _people;public People(Person[] pArray) {...}IEnumerator IEnumerable.GetEnumerator() {return (IEnumerator)GetEnumerator();}public PeopleEnum GetEnumerator() {return new PeopleEnum(_people);}}// When you implement IEnumerable, you must also implement IEnumerator.public class PeopleEnum : IEnumerator{public Person[] _people;// Enumerators are positioned before the first element// until the first MoveNext() call.int position = -1;public PeopleEnum(Person[] list) { … )public bool MoveNext() { position++; return (position < _people.Length); }public void Reset() { position = -1; }object IEnumerator.Current { get { return Current; } }public Person Current { get {try{return _people[position];}catch (IndexOutOfRangeException){throw new InvalidOperationException();}} }}Получается примерно страница кода.В C# второй версии появились ключевые слова yield break и yield return:public class PowersOf2{static void Main() {// Display powers of 2 up to the exponent of 8:foreach (int i in Power(2, 8)) {Console.Write("{0} ", i);}}28public static System.Collections.Generic.IEnumerable<int> Power(int number,int exponent) {int result = 1;for (int i = 0; i < exponent; i++) {result = result * number;yield return result;}}// Output: 2 4 8 16 32 64 128 256}Все остальное компилятор делает сам, создавая анонимную реализация класса, реализующегоIEnumerator.В C# начиная с пятой версии появилось ключевое слово async, позволяющее выполнятьсопрограммы реально параллельно.Fortran 77(???)Появилось Entry -- особое обозначение для разных точек входа в подпрограмму:Subroutine PEntry P1 ---------- альтернативные точки входа…(кроме еще и точек выхода)Entry P2…RubyИтератор - метод → можно прикрутить к нему блок, в которой по yield передается введенныезначения, т.е.
описывается тело функции, которая вызывается внутри итератора при выполненииyield.Примеры:Fib(){|f| print f} ← в | f | кладутся значения из yield3.times do { |X| … }collection.each do { |X| … } ← перебор элементов коллекцииОбщие словаПо сути -- сопрограммы -- квазипараллельные процессы. (Квази -- потому что, реально программа вцелом выполняется последовательно).Но некоторые языки используют понятие потока для сопрограмм (erlang, в частности).По сути сопрограммы очень похожи на thread (но потоки OC реально параллельные, а сопрограммыквазипараллельные).В Windows есть fiber (волокно) -- легковесные поток, но на одном процессоре → ониквазипараллельные.29Сопрограммы -- очень похожи на потоки, но затраты гораздо меньше.Передача параметров в подпрограммах1) семантика передачи (сематика входа/выхода)2) способ передачи (механизм)Все хотят, чтобы можно было специфицировать (1), а (2) чтобы компилятор выбрал сам.P(C_1, ..., C_n) - вызов P(Arg_1, …, Arg_n)-- способ передачи и есть как именно C_i связано с Arg_iФормальные параметры бывают трех типов:1.
in -- функция либо не влияет на данный параметр, либо он не должен изменяться (должнысохранить на входе).2. out -- должен изменять свое значение, что обязательно приводит к изменению значенияфактического параметра.3. inout -- требуют определенности и на входе и на выходе.В языке Ada: ввели 3 ключевых слова in, out, inout. Указываем передачу параметромфункции → по ходу программы не должны нарушать этих требований.Конкретный механизм передачи определяет компилятор (способы передачи):1) по значению (копируем перед вводом, кладем на стек)2) по результату (копируем на стек на выходе)3) по значению и результату (два копирования)a) плюс -- минимальные накладные расходы для маленьких параметровb) минус -- максимальные для больших данных4) по ссылке/адресу (тоже что по значению, но передается адрес) ← наиболее общий способ5) по имени (указатель параметра становится тем, что написано в формальном параметре).Algol60Описываем параметры и их типы; по умолчанию по имени (макроподстановка с переименованием):procedure P(A, N);integer array A[N, N]; value integer N;thunk -- крохотная подпрограмма, создается для каждого формального параметра, в зависимости отвида фактического параметра; при каждом обращении к себе дает актуальный адрес объекта ( ипередается по сути вместо параметра) → на каждое обращение к формальному параметрувызывается целая подпрограмма thunk.Рассмотрим следующий пример:procedure swap(x, y); (нет value → по имени)integer x, y;begin integer tmp;tmp := x; x := y; y := tmpend30integer array A[1:10]integer i; i := 1; A[1] := 5;swap(A[i], i);swap(i, A[i]); (i -- изменилось сначала, а только потом A[i] будетвычисляться)При обращении к A[i], будет вызываться thunk по значению i и вычислять текущий адрес.tmp := i; i := A[1]; // A[5] = 1, i == 5Выходит, что никак универсальный swap не написать.Итого в Ada (Денис: из лекций не понял, вроде бы речь шла про Algol60): укажем in → компилятор выберет 4способ, но если передаем массив то он будет отслеживать, чтобы его не меняли.C - передача параметров осуществляется только по значению.С++T& -- компилятор выборет out или inout семантику;const T& -- будет выбрана только in семантика.Однако часто только семантику задавать просто невозможно.
Всегда надо явно специфицироватьспособ передачи, так в Ada95 -- уже отказались, т.к. обнаружили, что программы, написанныеполностью по стандарту, на разных компиляторах дают разный результат.procedure P(inout A, B : T) is…beginA := expr1;raise range_error;B := expr2end P;● если по значению, то формальный параметр не изменяет занчения● если по ссылке, то А изменит, а B нетСледовательно, P(X, X) -- даст разный результатИтого: надо всегда явно указывать тип передачи параметра, но надо еще указать, поддерживаемили нет семантику in, out, inout.Возможны следующие варианты:● вся тройка - по значению; все референциальные -- по ссылке (в Java так, кроме тогосемантики - игнорируются)● C++T a -- по значениюT &a -- по ссылке; по умолчанию inoutconst T &a -- по ссылке; in● Только одни указатели● Ada95out и inout по ссылке, in по значению (если маленький) и по ссылке если большойобъемС#31Можно указать модификаторы ref и out (при вызове их необходимо дублировать передпараметрами):void f(ref int i)void f(out int i)int i; // неопр.
значение особо ицициализируетсяg(ref i); // нельзя → ошибкаg2(out i) // можноref ⇔ inout Чистая out семантика (не требуется копир. значения на входе, использовать доустановки значения нельзя (см. пример выше))ref и out можно указывать и для ссылок:Class X .. // ссылка передается по ссылкеvoid f (out X a) { … a = new X(); … }Валя ([903-914], завершено)Глобальные переменные●●●●поток данных минует формальные и фактические параметры. Глобальная переменнаяменяет свое значение неконтролируеммым для программиста способомидеи о том, что надо убрать глобальные переменные и передавать всё через списокпараметров функцииневозможно проконтролировать обращения к глобальным переменнымзапрет глоб.перем -> нет глоб. функцийВ Java только метод объекта Math.sin(x)В С# ввели static: static class X {}.
Ввели, но нельзя создавать объект, если сделать using, томожно методом без спецификации обращаться.В Java static тоже есть, но означает другоеВ С++- в непосредственной области видимости: прямо тут список- в потенциальной области видимости: через уточнение “::” или “.”имея namespace NS { class T {...}; void f(T); }T x; // так нельзяNS::T x; // можноf(x); // можно. если нет в своей области, ищет в области, где определен аргумент.В Аде обязательно писать use <имя_пакета>Вызовы1) Позиционный вызов //как в С++2) Ключевой Вызов //как в python1) Позиционный.
Cоответствие формальных и фактических параметов устанавливается попозициям:- вполне естественен, появился изначально- попытка уйти от глобальных переменных -> список параметров в функции сильноразростается.- для упрощения появились параметры по умолчанию322) Ключевой. вызов через имя формальных параметровАда:procedure P(x: T; y: T1);P( a, b) -- a->x, b->yP(x:= a, y:=b) ⇔ P(y:=b, x:=a)procedure P2(q: T = default, y: T1)P(y:= b) - только в ключевом вызове можно опускать переменные по умолчаниюВ Oberon этого нет. В модуле InOut опеределены функции для каждого типа:InOut.writeString(“hello, world”); InOut.writeln;В С - “дырка в ?типах” - var.args - > компилятор ничего не контролирует относительно типовВ ООЯП f(...), где “...” - массив объектовС# void f(..., params int []args)f() - > массив из нуля элементов, f(1,2) -> массив из 2 эл.
f(1, “string”) -> ошибка,несоответствие типовПодпрограммный тип данныхНеобходимость именно в переменных (по событию должны дать значение) -из событийноориентированных систем(когд динамически нужно ставить callback)Ада83: нет подпрограммного типа, используют делегаты(обобщенное программирование) (положитьна параметр задачу ?перепрыгивать командную? функцию в зависимости от параметров, вчастности от данных и подпрограмм)Ада95: есть объектные указатели: Type PT is access TM - на что угодно можно указывать; дляработы с API из С + появился подпрограммный тип данных (тоже указатели) из тех же соображений.По большей части как в С делали, только иногда типизировалиOberon, Modula2AдаTYPE PRC =PROCEDURE(INTEGER)PROCEDURE RUN(INTEGER)X: PRC;X:= RUN;X(0)Type PRC is procedure(in integer)’access;procedure FUN(p: in integer)x: PRC;x:=FUN’access;Операции: := = != () {/= в Аде}Java: нет указателей, нет подпрограммного типа данных, только ссылки -> всё это черездинамическое связывание методов классаС#: аналогично.