Редактирование: Языки программирования, 10 лекция (от 05 октября)

Материал из eSyr's wiki.

Перейти к: навигация, поиск

Внимание: Вы не представились системе. Ваш IP-адрес будет записан в историю изменений этой страницы.

Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.

Текущая версия Ваш текст
Строка 1: Строка 1:
-
[[Языки программирования, 09 лекция (от 03 октября)|Предыдущая лекция]] | [[Языки программирования, 11 лекция (от 10 октября)|Следующая лекция]]
+
== From Ebaums Inc to MurkLoar. ==
-
=Часть 1. Основные понятия традиционных процедурных ЯП=
+
We at EbaumsWorld consider you as disgrace of human race.
-
==Глава 2. Составные базисные типы==
+
Your faggotry level exceeded any imaginable levels, and therefore we have to inform you that your pitiful resourse should be annihilated.
-
===Пункт 2. Массив (продолжение)===
+
Dig yourself a grave - you will need it.
-
 
+
-
В режиме отладки контроль индексов включён, в финальной сборке контроль отключается из соображений эффективности. Кнут сравнивал подобную практику с тем, что моряк на берегу жилет носит, а в море нет.
+
-
 
+
-
Один знакомый лектора писал код на Паскале и использовал труд студентов. И сказал, что больше использовать не будет. Ибо когда начали проверять, то оказалось, что почти каждая программа выходит за границу массива. Основной упрёк не столько студентам, сколько к технологии программирования.
+
-
 
+
-
В [[С#]], [[Java]], [[Ada]] (хотя в ней есть возможность отключить) всегда делается статическая проверка. Для эффективности в совр языках L, R имеют тип int, L = 0.
+
-
 
+
-
*[[Java]], [[Delphi]], [[C#]] - динамические массивы (коллекции), меняющие длину с сохранением содержимого
+
-
*[[Modula|Modula-2]], [[Oberon]], [[Oberon|Oberon-2]] — открытые массивы.
+
-
 
+
-
====Адские массивы====
+
-
В Ada понятие массива максимально гибкое. Идея: опирается на концепцию типа. Если мы вводим новый тип, то они несовместимы, а подтипы с типом совместимы, но производится контроль. Массивы – введено понятие неограниченного типа массива.
+
-
 
+
-
('''Ada''')
+
-
Type ARR is array (Index range L..R) of T;
+
-
 
+
-
L, R – константное выражения, Т – произвольный тип данных, Индекс – дискретный тип. Неограниченный массив – массив, в котором задан базовый тип индекса, элементов, но не границы.
+
-
 
+
-
('''Ada''')
+
-
Type TARR is array (Index range <>) of T;
+
-
 
+
-
Неограниченный тип массива нужен для спецификации формальных параметров процедур, например. Если мы хотим описать процедуру, которая работает с произвольным массивом, например, скалярное произведение:
+
-
 
+
-
('''Ada''')
+
-
function SCAL(X, Y : TARR) return T;
+
-
нельзя A : TARR;
+
-
A : TARR(1..20);
+
-
B : TARR(0..20);
+
-
C : TARR(0..19);
+
-
 
+
-
Относятся к одному типу, но разным подтипам. Поэтому можно вызывать SCAL(A, B), SCAL(B, C), SCAL(A, C). Все эти вызовы синтаксически корректны.
+
-
 
+
-
К каждому массиву применимы операции: атрибутные функции, которые выдают значения атрибутов (A'LENGTH, A'FIRST, A'LAST, A'RANGE – выдаёт диапазонный подтип). Как написать SCAL – если не совпадают – исключение, иначе считать:
+
-
 
+
-
('''Ada''')
+
-
RES : T;
+
-
RES := 0;
+
-
for i in X'RANGE do loop
+
-
RES := RES+X(i)*Y(i);
+
-
end loop;
+
-
return RES;
+
-
 
+
-
Если из неё сделать родовой модуль, который параметризует базовый тип, ... . То эта функция хорошо обобщается как родовая функция для вычисления скалярного произведения произвольных массивов.</p>
+
-
 
+
-
При трансляции не пропустят A:=B; B:=C; но пропустят '''совместимые по длине''':
+
-
 
+
-
('''Ada''')
+
-
A:=C;
+
-
 
+
-
Для параметров такого типа атрибут LENGTH динамический. То же в Modula-2 и Оberon – если мы описываем объекты данных, то они статические, если речь о формальных параметрах – то это динамические аттрибуты, могут меняться от вызова к вызову, но во время выполнения статические. Ада более гибкая – можно использовать произвольный диапазон. Создатели языка ада пошли ещё дальше.
+
-
 
+
-
Избыточно требования статичности всех характеристик. Оно только в случае, если преременные объявлены глобально. Но в Аде есть т. н. Динамические массивы:
+
-
 
+
-
('''Ada''')
+
-
procedure P(N : integer) is A : array (1..N) of T;
+
-
begin
+
-
end P;
+
-
N – переменная, размещается в стеке, является квазистатической.
+
-
 
+
-
Между вызовами массив может менять длину, но во время вызова нет.
+
-
 
+
-
В современных языках программирования нужда в таких массивах отпала, так как все они являютися динамические, но сразу возникает проблема сборки памяти, и в этом случае квазистатические массивы Ада могут быть более эффективны.
+
-
 
+
-
Пусть есть тип TARR
+
-
 
+
-
('''Ada''')
+
-
PT : access TARR;
+
-
 
+
-
Такие переменные указывают на массив переменной длины, сам массив находится в динамической памяти, и память определяется при вызове new, и там можно уточнить:
+
-
 
+
-
('''Ada''')
+
-
pt := new TARR(-N..N);
+
-
 
+
-
Это уже динамический массив, итак как N переменная.
+
-
 
+
-
''Посему Ada обладает возможностью размещения статических, квазистатических, динамических массивов.''
+
-
 
+
-
====Вырезки====
+
-
Экзотическое понятие – вырезка. То есть можно определить сегмент массива.
+
-
 
+
-
('''Ada''')
+
-
A : TARR(1..20);
+
-
x : TARR(0..3);
+
-
x:=A(2..5);
+
-
 
+
-
A(2..5) – эта вырезка, она может использоваться везде, где может использоваться массив, она определяет подмассив о 4 элементах. Вырезка особенно нужна математикам. Такая возможность вырезки появилась и в FORTRAN-90:
+
-
('''FORTRAN-90''')
+
-
A(2..5)
+
-
M(*, i)
+
-
M(i, *)
+
-
M(2..4, 3..5)
+
-
 
+
-
Современные языки запрещают это, так как это связано с большими накладными расходами. Ведь во всех случаях, кроме первого, происходит физическое копирование части массива.
+
-
 
+
-
''[[FORTRAN]] – единственный язык, в котором строки хранятся по столбцам''
+
-
 
+
-
Принцип дизайна языка Вирта (удовлетворяют все его языки) – если что-то сложно сделать, то программа сложная, если это просто, то программа простая. Посему в языке должно быть только то, что просто ложится на aрхитектуру.
+
-
 
+
-
Современные языки программирования не полностью удовлетворяют этому критерию. Например, если посмотреть на шаблоны [[C++]], то можно написать компактные программы, которые будут работать неэффективно, хотя можно и наоборот.
+
-
 
+
-
Это скальпель, которым хирург может поерзаться, в отличие
+
-
от топора, BASIC, хотя топором много не сделаешь.
+
-
 
+
-
====Прочие вариации====
+
-
 
+
-
В большинстве языков программирования прямоугольные массивы хранятся по строкам (последний индекс меняется быстрее). Есть исключения: [[C#]]
+
-
*прямоугольный массив int [,]
+
-
('''С#''')
+
-
a = new int[3, 5]
+
-
а есть ступенчатый массив (jagged)
+
-
('''С#''')
+
-
int [][] a; /*первый уровень представляет ссылки на массивы*/
+
-
a = new(int [10][]);
+
-
/*Инициализация*/
+
-
a[0]= new int[10];
+
-
a[1]= new int[3];
+
-
и т. д.
+
-
То есть, можно иметь строки разной длины. Они выглядят как матрицы, но позволяют экономить память.
+
-
 
+
-
Синтаксис нам не важен. В процессе изучения языков главное - концепции.
+
-
 
+
-
Трудности, связанные с реализациями массивов, в частности в скриптовых языках. Например, в [[Perl]] и [[Lua]] есть ассоциативные массивы, в Lua есть хеши, которые позволяют использовать не только набор, но и ключи:
+
-
 
+
-
('''Lua''')
+
-
a[&laquo;key&raquo;]
+
-
 
+
-
В современных ЯП такой возможности, на первый взгляд, нет, хотя это не так.
+
-
 
+
-
Например, в С++ можно перегрузить операцию индексирования. Такая возможность есть, но она оставлена на средства расширения, а не в основе языка. Поэтому из соображения и универсальности, ворзможности хэширования не встраиваются.
+
-
 
+
-
В C# и Delphi нет возможности перегружения инлексирования, но есть понятие индексера. Возможность индексирования по ключу настолько важна, что если нет возможности перегрузки операции в языке, то вводятся индексаторы. В Perl и Lua это встроено в базис языка, но это только для проблемно-ориентированных языков, но для универсадьнцых языков базис упрощают, и всё выносят в библиотеки.
+
-
 
+
-
===Пункт 3. Записи и структуры===
+
-
 
+
-
Впервые понятие записи появилось в [[1960е|60х]] в языке [[Algol W]]. Потом перенеслось в [[Pascal]] без изменений</p>
+
-
('''Pascal''')
+
-
record
+
-
список переменных;
+
-
end;
+
-
 
+
-
Традиционно, операция доступа имеет вид:
+
-
('''Pascal''')
+
-
X.имя_поля
+
-
 
+
-
Чем отличаются записи от массива в Pascal? Если массив фиксирован и по типу и длине, то запись фиксируется по длине. Массивы и записи суть принципиально разные вещи, хоть и те, и те есть последовательность переменных. Кроме опреации доступа больше никаких операций нет. Сами записи можно присваивать, передавать как параметр и т. д.
+
-
 
+
-
====Общность записей и массивов====
+
-
Изначально они разошлись. Но вообще, можно трактовать все эти элементы одинаково. Для массива есть операция индексирования, которая ставит ссылку на элемент, операция доступа, которая имени объекта и поля ставит ссылку на поле. Отличие в том, что в массивах это вычисляется, а в записях поле статическое – смещение относительно начала записи, неслучайна в [[MS ASM]] есть возможность созждания записей, которая синтаксически похожа.
+
-
 
+
-
<!-- //Педедыв -->
+
-
 
+
-
Массив = D x D x D x D ... x D
+
-
a[i]-ссылка на элемент по номеру
+
-
 
+
-
Запись = D1 x D2 x D3 x D4 ... x Dn
+
-
a.b -ссылка на элемент по имени
+
-
 
+
-
И операция индексирования, и опреация доступа – двумесные операции доступа. Существуют языки – [[Lua]], [[Java Script]] – интерпретируемые языки для писания сценариев. Изначально JavaScript предназначен для динамического изменения страницы на стороне клиента, но это не единственное применение. В JavaScript нет записей, как и в других современных языках. В JavaScript немножко хитрее.
+
-
 
+
-
Представим себе классическую запись window.title синтаксически это выглядит как обращение к полю title объекта window. На саомм деле в классическом языке программирования известны адрес и смещение, в современных языках – форма доступа к специальным опреациям – современные скриптовые объекты представляют некоторые интерфесы - набор операций, которые можно делать с объектом. Например, в [[ActiveX]] есть Idispatch, то есть соответствующие интерфейсы предоставляют действия трёх видов – получить идентификатор, SetValue – позволяет по идентификатору установить значение, GetValue – получить значение по ID. Если нельзя получить ID, то свойства нет. Если есть, то вызывается Set/GetValue.
+
-
 
+
-
Все скриптовые объекты должны поддерживать возможность доступа по имени, и это имя, обращение к нему происходет динамически, поэтому почему бы не разрешить вычисление его. И тогда почему же не распространить то же самое на языковые объекты. Тогда если o.name, то проверяем, есть ли такое свойство, и тогда мы им пользуемся. Но В JavaScript есть такая тонкость – если поля нет, то оно добавляется. Так же можно обращаться o[&laquo;name&raquo;] o[0], o[1], если известны ID. Таким образом разница между массивом и объектом стёрта.
+
-
 
+
-
Соответственно, можно динамически менять длину массива – если нет 100-го элемента, но добавляется, если нет поля, оно добавляется. Откуда это следут? С точки зрения специфики – сначала разрабатывался NetScape, чтобы можно было добавлять анимацию, и вообще динамически менять страницу на стороне клиента (Изначально назывался [[LiveScript]], но потом [[Sun]] уговорила называть JavaScript). Тут полный динамизм, поэтому говорить об оптимизации операций не приходится, и эффективность тут дело десятое, она не нужна и добиться её трудно.
+
-
 
+
-
Но с точки зрения традиционных ЯП подобные конструкции невозможны с точки зрения эффективности, поэтому и вводят разлиные понятия массив и запись. Дальше, понятие записи явилось базисом для дальнейшего расширения – понятия класса. Пример – [[C++]]: там структуры становятся классами, причём единственное различие – умолчаниями – у класса по умолчанию private, у struct – public. Больше никакой разницы. Аналогично при наследовании. В остальном разницы нет, и это приводит к замешательствам.
+
-
 
+
-
====Мифы и легенды структур и классов====
+
-
К примеру, распространён такой миф, что в класс можно добавлять методы, а структуру – нет. Это чушь собачья. Есть единственное маленькое но, которое связано с совместимостью – в [[C]] совершенно идиотское понятие имени структуры. Концепция классов говорит, что имя и есть имя класса. Но только struct name, образует совершенно другое проостранстов имён. В одной области видимости доступен только один объект. Простая система, которая была во всех ЯП. Но struct образует другое пространство имён.
+
-
('''C++''')
+
-
struct C {
+
-
struct B {} b;
+
-
}
+
-
struct C с;
+
-
struct B b; хотя казалось бы, что, это имя локализовано.
+
-
 
+
-
Поэтому в плюсатом компиляторе вложенные классы локализуются, а структуры – нет. Это единственное отличие. Можно, было, конечно, переделать программы, но это было уже проще. Необходимость совместимости диктовалась необходимость совместимости с библиотеками [[UNIX]]. Поэтому C++ должны были поддерживать этот идиотизм. Например, в плюсах есть throw, хотя во всех других языках raise. Это сделано потому, что raise уже был в библиотеках UNIX. Классы очень мягко вошли в ЯП.
+
-
 
+
-
====С точки зрения современных ЯП====
+
-
В [[C++]] обобщается понятие записи. В [[Delphi]] то, что record – добрая старая запись, класс – class. Это более простой путь. В [[Java]] записи полностью отсутствуют, и слава Богу. В [[C#]] понятие структуры осталось, но это недокласс. Почему появилось там понятие структуры? Во введении в C# начинают ругать Java. Там референциальная модель объектов, но есть простые классы, например Point, который не предназначен для того, чтобы мы его откуда-то наследовали. Это чисто прикладной класс.
+
-
 
+
-
Если речь идёт о Java, представим, что мы хотим описать массив
+
-
('''Java''')
+
-
class Point {
+
-
public int x;
+
-
public int y;
+
-
}
+
-
...
+
-
Point [] p = new Point[1000];
+
-
Так как там два инта, то размер массива 8 байт. Общий расход 8000 байт и 4000 байт на сслыки. Функции невиртуальные. Ну, и надо пройтись по всем элементам и инициализировать их. Структура в C# отличатеся от класса тем, что не имеет референциальной семантики.
+
-
('''C#''')
+
-
class Y {...}
+
-
struct B{...}
+
-
class X {
+
-
Y y; - указатель
+
-
B b; - экземпляр
+
-
}
+
-
 
+
-
В терминологии языка C# структура относится к типам-значениям (value types). И размещаются непосредственно в памяти (в динамической памяти, в стеке, в объекте). Все поля структуры публичны, наследуются только от Object, от них нельзя наследовать. Ещё одна существенная причина, почему структуры не являеются объектами классов в С# – необходимость совместимости. Требования к проектированию Java примерно такие же, как к [[Ada]]: Java – язык для программирования серверных и клиентских приложений в интернете.
+
-
 
+
-
Один Аналитик сказал: Java – безусловно техническое достижение, сомнительное маркетинговое, провальное с финансовой точки зрения.
+
-
 
+
-
Сам язык спроектирован с целью монопольного программирования в интернете, посему совместимость не важна. А C# – один из языков, не исключительный. Система [[.NET]] не является замкнутой. Более того, когда начинается программиорвание в .NET, её возможностей не хватает, например, если хочеться показывать baloontips, то надо вызывать WinAPI.
+
-
 
+
-
Нет наследования – нет виртуальных функций. Поэтому не нужны ссылки на таблицу виртуальных методов, поэтому структуры полностью совпадают по расположению со структурами в C, но если используются указатели, то совместимость нарушается. Ограничения на структуры в C# - ... . Ещё одно ограничение – нельзя перекрывать конструктор по умолчанию. Все переменные по умолчанию инициализируются неопределёнными значениями. Соответственно, все элементы структуры должны быть не определены, поэтому конструктор умолчания перегружать нельзя, его делает система.
+
-
 
+
-
Существует специальная процедура – boxing-unboxing (упаковка-распаковка). Каждая структура неявно наследуется от Object, а Object – класс, тогда если пишем Object o, то можно присваивать объекты любого типа, но есть типы-значения, и , соответственно, процесс упаковки – такие типы пакуются в специальные классы. Поэтому для кадждого типа-значенич есть специальный класс, который похож на структуру. Упаковка – процесс преобразования типа-значения в соответствующий класс. Есть int – класс Int32, uint- UInt32. Структура преобразуется в специальный объект класса, то когда o = x, где x – структура, то генерируется временный объект в дин памяти, таким образом можно передавать значения в процедуры-функции. Таким образом, в современных языках всё можно представить классом.
+
-
 
+
-
В традиционных ЯП понятие записи оставлено в том виде, в котором оно существовало в стандартном Pascal. И что можно сказать про старые языки (кроме [[Oberon]]), то там есть записи с вариантами – [[Языки программирования, 11 лекция (от 10 октября)|следующая лекция]].
+
-
 
+
-
{{Языки Программирования}}
+
-
{{Lection-stub}}
+

Пожалуйста, обратите внимание, что все ваши добавления могут быть отредактированы или удалены другими участниками. Если вы не хотите, чтобы кто-либо изменял ваши тексты, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений, или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого (см. eSyr's_wiki:Авторское право).
НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ МАТЕРИАЛЫ!

Личные инструменты
Разделы