OSDev

для всех
Текущее время: 10 май 2024, 12:40

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 285 ]  На страницу Пред.  1 ... 3, 4, 5, 6, 7, 8, 9 ... 29  След.
Автор Сообщение
СообщениеДобавлено: 12 дек 2014, 22:53 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
Без ключевых слов будет путаница. Писать красиво надо уметь. Программирование это гуманитарная наука. Вот Ньютана почему запомнили? А Фоннеймона? Да породной простой причине они могли красиво излагать свои мысли, на простом и доступном языке. Хотя до них идеи излагали и другие авторы.
А почему Фейнман не сильно популярен? Писал просто, но путанно и с ошибками.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 дек 2014, 23:12 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
pavia писал(а):
А почему Фейнман не сильно популярен? Писал просто, но путанно и с ошибками.

[шутка] Что-то мне думается, Вы хотите заиметь славу тов. Фейнмана : ) [/шутка]


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 дек 2014, 23:19 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
[ 10 ] :: Сильная или слабая типизация?

Есть две крайности: запрещать неявное приведение типов или разрешать его. В первом случае получаем проблему с раздуванием кода из-за необходимости делать несколько функций, когда тип данных отличается, а смысл остаётся (чистый язык Pascal), во втором случае получаем кучу проблем с безопасностью приведения типов (C/C++). Первая проблема решается полиморфной типизацией, но тут есть две трудности: если делать как в C++ через шаблоны, то получаем «полиморфизм», реализованный с помощью мономорфизации (раздувание исполняемых файлов), а если язык поддерживает полиморфизм типов, то он скорее всего неэффективен (не может быть эффективно скомпилирован или это чистый интерпретатор). Или кто-то знает случаи, когда поддержка настоящего полиморфизма даёт такой же эффективный код как в случае мономорфизации? Я не знаю.

Я считаю, что нужно запретить неявное преобразование типов. Язык C, в котором оно присутствует, это не язык высокого уровня, это скорее такой плагин к ассемблеру (один чёрт знает, почему типы в нём называются char, short int, int, long int, которые фиг пойми, что означают, особенно когда signed char и char относятся друг к другу совершенно не так же, как singed int и int).

Разумеется, заставлять программиста писать свой алгоритм приведения знакового типа к беззнаковому тоже нельзя. Нужна инструкция, которая разрешает компилятору приводить типы, но чтобы программист сам явно её использовал, полностью отдавая себе отчёт о возможных проблемах. Аналог static_cast в C++. Для приведения типов по этим функциям должен быть чёткий стандарт, который прописывает все возможные случаи потери данных или появления неожиданностей. При этом приводить всё подряд друг к другу этими функциями нельзя.

Например, нельзя привести число к типу указателя. Указатель – это не число в языке высокого уровня, это указатель, хотя для машины и нет разницы. Ноль – это ноль. Нулевой указатель – это совершенно отдельный тип, который нельзя привести к нулю. True – это true, его нельзя привести к числу 1 или 2 или любому другому, как fasle нельзя считать нулём. Результат выполнения логической операции всегда true или false (если речь не идёт о нечёткой логике, вряд ли она будет иметь место в условных конструкциях языка).

А что можно? Приводить можно, например, числа к числам, но факт приведения нужно указывать явно через static_cast (или аналог). Если приведение с потерей данных, или с округлением, должен быть стандарт на потерю данных и на округление.

Таким образом, компилятор разрешает приводить один тип к другому только в том случае, когда программист либо пишет свою функцию приведения (explicit constructor), либо использует встроенную static_cast (и аналоги) для «очевидных» приведений встроенных в язык числовых типов.

Из всех решений, которые я рассматривал, параметрический полиморфизм через шаблоны в сильно типизированном языке оказывается наиболее безболезненным, он хоть и порождает раздувание исполняемого файла, но обеспечивает неплохое повторное использование кода и разумную универсальность.

Типизация должна быть статической. Программист явно задаёт типы данных переменным до их использования, но при этом вполне можно работать с ключевым словом auto, если тип данных выводится из контекста при компиляции, как это иногда бывает в C++.

Я вот только не определился, нужна ли нам конструкция типа void * или reinterpret_cast? Всё-таки эти штуковины чем-то сродни inline-assembler'y со всеми вытекающими последствиями. Нелогично запрещать inline-assembler и оставлять возможность хакерских приёмов над данными в памяти.

[ Продолжение следует ]


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 13 дек 2014, 06:56 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
Неявные приведения должны быть запрещены -- без этого огроменная дыра в надёжности и безопасности. В то же время нельзя закрывать возможность написать "кривую" программу, если это программисту нужно, а соответственно, должны быть явные приведения чего угодно во что угодно и возможность манипулировать с указателями. Но, запрещая, не надо доводить это до полной крайности. В Аде, например, если у тебя переменные вещественного типа, ты не можешь написать: A := B + 2; -- компилятор ругнётся на целочисленную константу, поскольку для вещественного надо писать 2.0. ИМХО, это уже перебор с погоней за надёжностью. Как по мне, должны автоматически выполняться преобразования меньших типов в логически аналогичные большие (байт в слово), поскольку эти преобразования всегда корректны -- но с выдачей предупреждения, которое можно либо отключить, либо превратить в ошибку с помощью параметров транслятора.

А вот насчёт "плагина к ассемблеру" Вы не правы. Си -- язык высокого уровня, поскольку низкий уровень -- это только те языки, которые прямо отражают архитектуру и систему команд конкретного процессора. Конечно, если вводить не два, а больше уровней языков, то Си будет болтаться где-то в нижней части -- но всё ж выше любого ассемблера.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 13 дек 2014, 08:26 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
Да, пожалуй, Вы правы. Действительно, ведь есть ещё очевидное приведение переменной одного типа в объемлющий тип. Негоже, например, преобразовывать переменную из вещественного типа в комплексный явно: c=static_cast<complex>(r)+4i (если c - комплексное число, а r - вещественное). Хочется написать просто c=r+4i или c=r, если комплексная часть равна нулю.

Тогда нужен ещё ясный стандарт, описывающий иерархию вложенных друг в друга типов. Но не всё так просто, как в математике, когда у нас множества чисел можно отсортировать по вложенности: N -> Z -> Q -> R -> C. Например, тип int128 нельзя привести к double, потому как double не объемлет целые числа такого размера. Здесь компилятор, на мой взгляд, должен жёстко выдавать ошибку с пояснением "потеря точности" и требованием явно указать, что программист пошёл на этот шаг умышленно. Далее, тип complex может быть над полем Q, а может быть над полем R, в зависимости от того, хотим ли мы точные комплексные дроби или приближённые. Поля R и Q вряд ли можно приводить друг к другу неявно... хотя, из double в rational можно, но смысл?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 13 дек 2014, 08:49 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
Zealint писал(а):
Тогда нужен ещё ясный стандарт, описывающий иерархию вложенных друг в друга типов. Но не всё так просто, как в математике, когда у нас множества чисел можно отсортировать по вложенности: N -> Z -> Q -> R -> C. Например, тип int128 нельзя привести к double, потому как double не объемлет целые числа такого размера.


Естественно, всё это должно быть. Замечу попутно, что приведение в double вызовет потерю точности не только для int128, но и для int64, ведь double имеет размер 64 бита, но, в отличие от целочисленного типа, в эти биты входит не только мантисса (которая, собственно, и определяет количество знаков числа), но ещё и порядок. Так что без ругательств можно приводить лишь int32 к double.

Ну и на всякий случай повторю сказанное мной выше: надо иметь возможность отключать контроль подобных вещей компилятором, причём отключать как ключом (для всей трансляции вообще), так и прагмой (директивой транслятора) -- для ограниченного участка кода. В Аде меня больше всего раздражает именно невозможность отключения контроля разных подобных случаев, а значит, необходимость постоянно ручками писать приведение типов -- хотя для меня это всё равно много лучше, чем полная бесконтрольность в Си (пару раз она позволяла мне поймать ошибку на стадии трансляции, хотя в 99% случаев тревога была, естественно, ложной).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 13 дек 2014, 22:23 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
[ 11 ] :: Константы в программе

В связи с наличием более-менее строгой типизации, возникает вопрос с тем, как транслятор должен интерпретировать неименованные константы в программе. Допустим, написано выражение
Код:
Int32 a = 1024;

Здесь всё ясно, компилятор видит, что происходит инициализация целого типа размером 32 бита и вопроса о типе числа 1024 не возникает. А теперь мы делаем иначе:
Код:
Int32 a = 10 + (65536*65536);

Каким должен быть типа произведения в скобках? Должен ли компилятор догадаться по размеру результата, что тут нужен тип более высокий, чем int32? Если да, то получаем ошибку неявного приведения типов. Если нет, то будет переполнение и это вряд ли то, чего хотел бы программист (возможно, он допустил опечатку - и компилятор должен что-то ему сказать).

Более сложный вопрос
Код:
Int32 a = 10 + (65536*65536)/65536;

Это выражение будет ли вычислено правильно? Формально, тип ответа сходится по размерности, но промежуточные результаты выходят за его рамки. Что должен делать компилятор? Чем по-умолчанию должно быть число 65536?

Чтобы не было подобных проблем, можно попытаться сделать следующее.

1 Запретить в программе все числовые неименованные константы, кроме 0 и 1. Правила хорошего тона (хотя у каждого они бывают свои) требуют наделять все константы, отличные от 0 и 1 именами, а когда мы даём константе имя, мы даём ей и тип, а значит сразу избавляем компилятор от лишних размышлений и на страже безопасности будет строгая типизация, а неизбежность переполнения компилятор может определить сам и выдать предупреждение.

2 Не запрещать неименованные константы в программе, но создать суффиксы, позволяющие однозначно определить тип (как в C++, только не через ж... ). Например (это лишь пример!)
Код:
65536i32 // целое 32 бита со знаком
65536i32u // целое 32 бита без знака
65536i16 // ошибка
1r // рациональное число 1/1
2+3i // комплексное

Идея с суффиксами шла к успеху, однако напоролась на непонятный момент: как обозначить комплексное число из рациональных чисел и как сделать его же из вещественны чисел? Если rational и real имеют одну и ту же первую букву, то ... использовать q? Тогда почему целое – это i, а не z? Можно сделать z, но тогда целое без знака логично было бы назвать n (натуральное), но среди натуральных чисел нет нуля! Тогда обозначим их через N0? : ) В общем, начинается головная боль. Такие числа труднее читать и понять логику суффиксов будет не всегда просто.

Таким образом, мне суффиксы не нравятся – это как костыли, которые придумывают за неимением более хорошего решения. В графическом редакторе можно было бы хотя бы создать выделение цветом или придумать нижний индекс, всплывающие подсказки и т. д. А в текстовом редакторе очевидных хороших решений я не вижу.

3 Выполнять явное указание типа при использовании неименованной константы, например, int32u ( 65536 ) – всё ясно, целое без знака 32 бита, определяется во время компиляции. Но это громоздко.

А что делать, когда пишется a [ 10 ]? Какой тип имеет число 10?

Здесь проблем нет, оператор [] получает на вход совершенно чёткий тип данных (или несколько, если у нас полиморфизм), определённый заранее. По умолчанию при обращении к элементам массива параметр номера элемента следует сделать такой же разрядности, какой разрядности у нас доступно адресное пространство. Если 4 Гб, то 32 бита без знака. Если больше, то 64 (понятно, что реально памяти меньше, но она же виртуальная). Поэтому обращение к элементам массива типа a [ i + 10 ] не вызовет проблем, если 10 будет приведено к типу i и может быть выполнено сложение, возвращающее результат, соответствующий ожидаемому для оператора [].

Среди трёх описанных решений мне больше нравится запрет на использование неименованных констант с возможностью явного указания типа, если такая константа всё-таки требуется. Но если она всё-таки требуется, значит (имхо!) код плохо продуман, то есть раз делаем костыль, значит и выглядеть он должен как костыль, то есть плохо. У этого костыля тип должен быть прописан явно. А то что за костыль без типа?

--- Лирическое отступление ---

Вообще, я сторонник того, чтобы хорошо продуманная программа могла быть написана хорошо, а плохо продуманная всегда писалась бы плохо и выглядела бы неопрятно. Такая философия. Я часто замечал за собой, что когда код программы начинает превращаться во что-то некрасивое (субъективно), значит решение (даже если будет правильным) по сути плохое, значит есть гораздо более хорошее. Данная философия нередко позволяла мне создавать очень хорошие программы. Я просто переписывал код так, чтобы он был красивым, и на удивление почти всегда программа получалась быстрее и проще (именно «и», а не «или»). Мистика.

--- Конец лирического отступления ---

Ещё один важный момент, в языке должна быть предусмотрена возможность визуального разделения разрядов (по желанию программиста). Хотя бы как в новом стандарте C++:
Код:
int a = 65'536+1'024
double b = 123'456.789'101'11.


Замечание! Записи типа
Код:
double a = .5
double a=5.

запретить и выжечь калёным железом. Меня лично они чем-то сильно задевают (это полностью субъективная оценка, но разумных обоснований я не слышал и не придумал).

Теперь что делать с константами в других системах счисления? Вот здесь как раз суффиксы и будут наиболее удобным решением: b, o, h. Например,
Цитата:
int16u a = AB'CD'h // шестнадцатеричное
int16u a = 1010'1011'1100'1101'b // двоичное, не может быть конфликта с шестнадцатеричным из-за суффикса b, так как тут нет суффикса h

Есть одна проблема, как не перепутать имя переменной ABCDh с шестнадцатеричной константой? Я думаю, что апостроф нужен перед суффиксом всегда, то есть константа должна иметь вид ABCD'h, без апострофа это будет литерал. Разумеется, апостроф - это лишь пример, выдумать способ разделения можно совершенно любой, лишь бы было удобно. В языке TeX, например, это команда «\,» которая раскрывается в маленький пробел, заметный глазу.

Теоретически, можно использовать даже произвольное основание int32 a = 123_5, что будет означать 123 в пятеричной системе, а int32 a = 123_3 будет уже ошибкой компиляции. Но, мне кажется, поддержка произвольной системы счисления на уровне языка не нужна, отдельной библиотекой с использованием строковых типов данных это сделать куда проще и понятнее. Во-первых, не нужно перегружать язык, во-вторых, букв всего 26, а систем счисления бесконечно, не говоря уже о нестандартных вроде i*sqrt(5).

[ Продолжение следует ]


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 13 дек 2014, 22:28 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
SII писал(а):
Естественно, всё это должно быть. Замечу попутно, что приведение в double вызовет потерю точности не только для int128, но и для int64, ведь double имеет размер 64 бита, но, в отличие от целочисленного типа, в эти биты входит не только мантисса (которая, собственно, и определяет количество знаков числа), но ещё и порядок. Так что без ругательств можно приводить лишь int32 к double.

Благодарю за замечание. Я подразумевал, что для некоторых читателей привычным будет считать double 10 или даже 12 байт (мало ли где такие бывают, а я знаю, что бывают), поэтому во избежании лишних пояснений сразу привёл пример, где всё ясно без дополнительных уточнений относительно размера double.

SII писал(а):
Ну и на всякий случай повторю сказанное мной выше: надо иметь возможность отключать контроль подобных вещей компилятором, причём отключать как ключом (для всей трансляции вообще), так и прагмой (директивой транслятора) -- для ограниченного участка кода.

Это вопрос идеологии. Мы разные программисты и по-разному овладели своим мастерством. В моей концепции разрешать отключать контроль нельзя, потому как необходимость нарушить контроль для меня - явный сигнал ошибки проектирования кода. А значит такой код лучше не писать вовсе. Но, повторюсь, я - это я.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 13 дек 2014, 23:01 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
Цитата:
Благодарю за замечание. Я подразумевал, что для некоторых читателей привычным будет считать double 10 или даже 12 байт (мало ли где такие бывают, а я знаю, что бывают), поэтому во избежании лишних пояснений сразу привёл пример, где всё ясно без дополнительных уточнений относительно размера double.

Забудьте. Double это 8 байт уже более 30 лет. Конечно иногда можно встретить и другие варианты. Но они устарели. Есть стандарты и их надо придерживаться. на числа с плавающий точкой таких 2 основных стандарта.
IEEE-754 (32 and 64 bit) and IEEE-854 (variable width)


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 13 дек 2014, 23:37 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
Zealint писал(а):
В связи с наличием более-менее строгой типизации, возникает вопрос с тем, как транслятор должен интерпретировать неименованные константы в программе


В Аде очень просто: есть внутренний тип компилятора ("истинный целый", так сказать); именно к нему и будут относиться константы, для которых тип не задан явно. Этот тип автоматически совместим с любыми подтипами этого внутреннего типа (а любые явные целые типы являются там подтипами внутреннего целого типа, любые вещественными типы -- подтипами внутреннего вещественного). Проблем с разрядностью/точностью возникнуть не может, поскольку здесь мы имеем дело с константой, а значит, компилятор точно знает, сколько под неё необходимо места, и может сравнить потребное место с реально задаваемым (например, при присваивании значении константы переменной).

Цитата:
В моей концепции разрешать отключать контроль нельзя, потому как необходимость нарушить контроль для меня - явный сигнал ошибки проектирования кода. А значит такой код лучше не писать вовсе.


У Вас слишком категорический подход. Может, он и годится для чисто математических задач, но в общем случае он порочен. Если язык проектируется не для использования в какой-то узкой области, а как достаточно универсальный, он должен давать возможность делать всё, что только вообще возможно -- просто он должен препятствовать совершению глупых или случайных ошибок. Случайно написать & вместо && или наоборот вполне возможно -- а потом лови такую ошибку. А вот случайно написать reinterpret_cast иди ещё что-нибудь подобное невозможно -- а значит, такие средства можно смело предоставлять программисту. Ну а если он дурак и не умеет пользоваться своим инструментом, это уже его проблемы: медицина здесь бессильна.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 285 ]  На страницу Пред.  1 ... 3, 4, 5, 6, 7, 8, 9 ... 29  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 109


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB