лекции (2008) (by Михайлишин Алексей_ Жбанков Денис_ Щербинин Виктор_ Чеботарев Павел) (1160831), страница 5
Текст из файла (страница 5)
Это и есть открытые массивы.Ада – неограниченные массивы.type arr is ARRAY (index range <>) of D; //A’RANGE – статический атрибутx: ARRUNL range -10..10; // A’RANGE – динамический атрибут для ARRUNLС#, Java, Delphi (совместимость с Pascal оставлена)Считается, что тип индекса int, в диапазоне от 0..N-1. Length – атрибут экземпляра, а не типа.T []x = new T[N]; //N – не обязательно константа0 <= I <= x.Length[lect13] Многомерные массивыПоддержка массивов это всегда компромисс между эффективностью и надежностью (например, проверкаout-of-range при каждом обращении).
Длина массива - это свойство не типа, а экземпляра. Названиемассива - это ссылка на экземпляр. Поэтому в C#, Java, Delphi нельзя объявить массив с уже прописаннойдлиной (надо сделать new). Поэтому менять длину массива во время исполнения нельзя, несмотря на то,что массивы выделяются из динамической памяти.«Вырезка» (Slice) – PL/I, FORTRAN 90, АдаA: Array (1..10) of T;A(2..5); //вырезкаA(*, i); //i-й столбецA(2..5, 1..3); //некоторая матрица – подтип исходнойМногомерный массив – фактически просто последовательность одномерных массивов (массив массивов).x[i][j] ~ x[i,j] – эквивалентно в Pascal, элементы по строкамIII) ЗаписиУ массивов основная операция – индексирование [], а у элементов записи есть операция name.id, котораяприменима слева к объекту типа «запись», а справа – к имени поля.
Синтаксис записи во всех языкахпрактически одинаков. Если мы заключаем набор объявлений переменных в скобки, то эта совокупностьобъявлений считается локализованной в записи, и доступ к ним может быть только по имени записи.Pascal:type R = records, y: T;i, j: integer;end;var rec: R;Ада:record//последовательность полей;end record;C:struct tag{//объявления полей;}Записи - это естественное обобщение классов. Но у класса могут быть еще и методы.
Класс носит нетолько функцию типа данных, но и модуля. Вместо механизма объединения типов есть наследованиеклассов. Пустой базовый класс не является ошибкой. Объединение осталось в С++ для совместимости с Си.А в Аде есть параметрические записи. В Java понятие записи вообще убирается. Структура отличается откласса в С++. В классе наследование и доступ по умолчанию приватные, в структуре - публичные. Именаклассов обязаны быть уникальными, а имя структуры - нет (если класс описан при помощи структуры).В C# понятие записи совершенно другое. Есть понятие value type и reference type структур нереференциальные типы данных.точка - point x,yБыстрее инициализировать массив point, нежели массив ссылок на структуры point.Структуры - недокласс в C#.
Структура не может наследовать и наследоваться. Не существует виртуальныхметодов структуры, как любой класс может содержать функции-члены. У структуры нельзя переопределятьконструктор умолчания (конструктор без параметров), так как в C# любой объект не имеет начальногозначения.В объектных языках (C#, Java) все является объектами. В С++ стандартные типы объектами не являются.Проблема Януса: разные объекты могут выступать в разных ролях. Ярче всего проявляется припроектировании интерфейсов. Например, окно может содержать набор элементов, зависящий отконкретного окна.recordпостоянная часть;вариантная часть;end record;Постоянная часть - последовательность объявления переменных.Вариантная часть:• размеченное распределение (есть дискриминант, определяющий вид поля)• не размеченноеКак в Паскале получить доступ к биту? Никак? Нет, для этого есть неразмеченная вариантная часть.packed array of boolean; //это и есть последовательность битовПаскаль:type BitCell = recordcase boolean oftrue: (i: integer);false: (bits: packed array [1..48] of boolean);end;x: BitCell;x.i := 249;x.bits[48] := 1;[lect14] Объединение типов••регулярные - дискриминантнерегулярныеC:Union tag { T1 v1, T2 v2, Tn, vn };CaseV1:V2:V3:дискриминант of(объявления полей);(объявления полей);(объявления полей);А каков размер объединения? В Си он выбирается абсолютно естественно – по размеру максимальногоэлемента.
В Pascal память выделяется строго по тому варианту, который указан в new(P, t). Если памятьраспределена по одному варианту, а мы обращаемся по другому, то возникает ошибка. Такая ошибка необрабатывается компилятором т.к. это сложно, а в некоторых случаях невозможно.new(P, t); //присваивания не происходит т.к. может быть неразмеченноP^.D := t; //не стоит это забыватьПараметризованная запись (Ада):type VARREC(D: DT) is recordпостоянная частьcase D: DT ofwhen v1 => (вариант 1)when v2|v3|v5…v10: (вариант 2)when others…end recordx: VARREC(t); //t: DT;y: VARREC; //ошибка!Формально, параметр D можно поменять, но на практике – нельзя (выделение памяти происходит приинициализации, и менять значение дискриминанта не получится).
Память здесь выделяет некий зачатокконструкторов. В современных ООП нет нужды в записях с вариантами, если их оставили, то только длясовместимости (Delphi, C). В Обероне, Java, C# такой нужды нет, так в них нет проблем совместимости,нужные функции здесь выполняются, используя наследование.Основной недостаток записей с вариантами, объединений типов: они объявляются один раз, аиспользуются много раз – поэтому при модификации кода (добавление событий) появляется много проблем(сложно добавить новое во все переключатели, можно забыть).
Отсюда появляется ненадежность. Реальнорешает эту проблему только динамическое связывание методов. В идеале для этого нам нужныисходники только базового класса, и того, который мы добавляем.Для совместимости в С++ struct – это не совсем класс. По умолчанию это есть класс, но разрешенысовпадения имен. Если такие конфликты встречаются, то доступ к структуре осуществляется только поключевому слову struct.Пример: если мы объявляем класс Point, содержащий две координаты (+какие то методы), то для передачимассива из Point в функцию мы выделяем память под массив указателей и по два инта на каждый указатель.То же самое при удалении. Эта проблема появилась в C#, Java, но ее нет (и не может быть) в С++ (там нетсборщика мусора и проч.).
Для решения этой проблемы (производительности) введено слово struct, но этоне то же самое, что в C++. В итоге упрощается процедура распределения памяти и увеличиваетсяпроизводительность. С точки зрения синтаксиса struct не отличается от классов, но не с точки зренияфункциональности (нет наследования, другое распределение памяти, запрещено переопределятьконструктор по умолчанию).Итог: запись (с вариантами) – устаревшее понятие. В современных языках понятие записи переросло впонятие класса.IV) Другие составные типы данныхЭти типы данных в современных ЯП мигрировали в библиотеки.•••абстракция I/O (файлы и т.д.)writeln в Паскале – выглядит как процедура, а на самом деле просто ключевое словомножества, таблицы (есть только некоторые исключения: SETL, М-2, Паскаль)строки (во всех языках тип данных string встроен в базис, исключение – С++, потому что он оченьмощный) – особенный типа данных, для него важна эффективная реализация (например,присваивание – просто копирование указателей)[lect15] V) О единстве составных типов данных••[] – операция индексирования, A x I -> Val (в С++ Val&).
– операция «точка», фактически частный случай индексированияС точки зрения общего подхода, множества – частный случай массивов, где элементы – объекты.Если мы обращаемся к объекту от неопределенного индекса, то он создается (мы разрешили неограниченноразрастаться как объектам, так и массивам). Если нам не хватает определенного свойства – то мы можемввести его динамически. За счет этого сильно увеличивается гибкость языка, но становитьсязатруднительно контролировать объекты => падает эффективность и надежность.obj.prop=-1; <=> obj[“prop”]=-1; //если свойства prop нет, то эта конструкция его добавляет5.prop; //что делать? придавать свойство prop всему классу Integer или только экземпляру?Т.е. мы можем трактовать массивы и структуры унифицировано.Глава 3.
Управление последовательностью действийТрадиционные ЯП основываются на архитектуре Фон Неймана, основные понятия которой – состояние ипоток управления («control flow» instead of «data flow» in func langs).I) Эволюция понятия ПУУровни ПУ• внутри выражения• между операторами программы• между модулямиПоток управления определяется, во-первых, приоритетами операций. Интересен другой вопрос: f(a, b) – вкаком порядке будут вычисляться операнды? В современных языках программа, зависящая от порядкавычисления операндов (каких-либо операций, кроме логических), считается нестандартизованной инепереносимой:*--*cp++; //даже не все компиляторы могут понятьWHILE (A[I] != X AND I < N) …; //ошибка или нет зависит от порядка вычисленияИмеет смысл зафиксировать ленивость логических операций (например, a and b – если a == false, то b ненужно вычислять, т.к.