Oberon space
General Category => Общий раздел => Тема начата: vlad от Октябрь 11, 2013, 05:54:37 am
-
Итак, pure Oberon-07 (без динамических массивов). Пытаемся написать модуль для работы со строками любого размера. Минимальный варинт:
- создать строку из ARRAY OF CHAR
- получить размер за O(1)
- получить CHAR по индексу (не знаю как сделать за O(1), но можно попытаться сделать меньше, чем за O(n))
Вот, что у меня получилось:
MODULE String;
CONST
offs8 = 8;
offs16 = 8 + 16;
offs32 = offs16 + 32;
offs64 = offs32 + 64;
offs128 = offs64 + 128;
offs256 = offs128 + 256;
offs512 = offs256 + 512;
offs1024 = offs512 + 1024;
chunkSize = 4096;
TYPE
Type* = POINTER TO RECORD
size: INTEGER;
data: ARRAY 8 OF CHAR;
next: POINTER TO Data16
END;
Data16 = RECORD
data: ARRAY 16 OF CHAR;
next: POINTER TO Data32
END;
Data32 = RECORD
data: ARRAY 32 OF CHAR;
next: POINTER TO Data64
END;
Data64 = RECORD
data: ARRAY 64 OF CHAR;
next: POINTER TO Data128
END;
Data128 = RECORD
data: ARRAY 128 OF CHAR;
next: POINTER TO Data256
END;
Data256 = RECORD
data: ARRAY 256 OF CHAR;
next: POINTER TO Data512
END;
Data512 = RECORD
data: ARRAY 512 OF CHAR;
next: POINTER TO Data1024
END;
Data1024 = RECORD
data: ARRAY 1024 OF CHAR;
next: POINTER TO Chunk
END;
Chunk = RECORD
data: ARRAY chunkSize OF CHAR;
next: POINTER TO Chunk
END;
PROCEDURE fromArray*(a: ARRAY OF CHAR): Type;
VAR
result: Type;
PROCEDURE copy(from: INTEGER; VAR dst: ARRAY OF CHAR): BOOLEAN;
VAR
i: INTEGER;
BEGIN
WHILE (i < LEN(dst)) & (i + from < LEN(a)) DO
dst[i] := a[i + from];
INC(i);
END;
RETURN i + from < LEN(a)
END copy;
PROCEDURE copyToChunks();
VAR
i: INTEGER;
from: INTEGER;
chunk: POINTER TO Chunk;
BEGIN
from := offs1024;
NEW(chunk);
result.next.next.next.next.next.next.next.next := chunk;
WHILE from < LEN(a) DO
IF i = LEN(chunk.data) THEN
NEW(chunk.next);
chunk := chunk.next;
i := 0;
END;
chunk.data[i] := a[from];
INC(i);
INC(from);
END;
END copyToChunks;
BEGIN
NEW(result);
IF copy(0, result.data) THEN
NEW(result.next);
IF copy(offs8, result.next.data) THEN
NEW(result.next.next);
IF copy(offs16, result.next.next.data) THEN
NEW(result.next.next.next);
IF copy(offs32, result.next.next.next.data) THEN
NEW(result.next.next.next.next);
IF copy(offs64, result.next.next.next.next.data) THEN
NEW(result.next.next.next.next.next);
IF copy(offs128, result.next.next.next.next.next.data) THEN
NEW(result.next.next.next.next.next.next);
IF copy(offs256, result.next.next.next.next.next.next.data) THEN
NEW(result.next.next.next.next.next.next.next);
IF copy(offs512, result.next.next.next.next.next.next.next.data) THEN
copyToChunks();
END;
END;
END;
END;
END;
END;
END;
END;
result.size := LEN(a);
RETURN result
END fromArray;
PROCEDURE size*(s: Type): INTEGER;
RETURN s.size
END size;
PROCEDURE at*(s: Type; index: INTEGER): CHAR;
VAR
result: CHAR;
PROCEDURE chunkAt(): CHAR;
VAR
i: INTEGER;
chunk: POINTER TO Chunk;
BEGIN
chunk := s.next.next.next.next.next.next.next.next;
FOR i := 0 TO ((index - offs1024) DIV chunkSize) - 1 DO
chunk := chunk.next;
END;
RETURN chunk.data[(index - offs1024) MOD chunkSize]
END chunkAt;
BEGIN
IF index < offs8 THEN
result := s.data[index];
ELSIF index < offs16 THEN
result := s.next.data[index - offs8];
ELSIF index < offs32 THEN
result := s.next.next.data[index - offs16];
ELSIF index < offs64 THEN
result := s.next.next.next.data[index - offs32];
ELSIF index < offs128 THEN
result := s.next.next.next.next.data[index - offs64];
ELSIF index < offs256 THEN
result := s.next.next.next.next.next.data[index - offs128];
ELSIF index < offs512 THEN
result := s.next.next.next.next.next.next.data[index - offs256];
ELSIF index < offs1024 THEN
result := s.next.next.next.next.next.next.next.data[index - offs512];
ELSE
result := chunkAt();
END
RETURN result
END at;
END String.
Тестируем это безобразие:
MODULE TestString;
IMPORT String;
CONST
longStringSize = 1024 * 100 + 1;
VAR
s: String.Type;
testArray: ARRAY longStringSize OF CHAR;
i: INTEGER;
BEGIN
s := String.fromArray("abc");
ASSERT(String.size(s) = 3);
ASSERT(String.at(s, 0) = "a");
ASSERT(String.at(s, 1) = "b");
ASSERT(String.at(s, 2) = "c");
FOR i := 0 TO LEN(testArray) - 1 DO
testArray[i] := CHR(i);
END;
s := String.fromArray(testArray);
ASSERT(String.size(s) = LEN(testArray));
FOR i := 0 TO LEN(testArray) - 1 DO
ASSERT(testArray[i] = String.at(s, i));
END;
END TestString.
-
Что не нравится в этом решении (помио отсутствия динамических массивов, которые сделали бы решение тривиальным):
- Жуткий копипаст (привет джерекам).
- Огроменная лестница IF'ов (из-за единственности RETURN). Можно завести булевую переменную и проверять ее каждый раз... но зачем?
- Запись String.at(s, index) для доступа к элементу строки осилят только религиозно обработанные писатели (привет перегрузке операторов).
- Строка всегда будет в хипе (давайте представим, что транслируем не в JS). И с этим ничего нельзя сделать, если потребовать иммутабельности строки (мутабельные строки не нужны). Привет конструкторам.
- Доступ к элементу за O(n), хотя n и поделено на довольно большую константу. Более быстрый доступ требует более сложной реализации.
-
Извиняюсь, что не в тему:
Не могу сообразить, какие типы использовать, чтобы получить следующее:
Нужна таблица, в которой одна из колонок была бы ссылка на объект составного типа (в терминах 1С).
Таблица, как понимаю МАССИВ. Колоноки обеспечиваются ЗАПИСЬю
Но вот не соображу, как задать СВОЙСТВО данной ЗАПИСИ составного типа (чтобы можно было присвоить, например и ТИП1 и ТИП2)
-
И вдогонку, в итоге нужна будет одна функция, возвращающая объекты разных типов. Это реализуется с помощью ПРОЦЕДУРНОГО типа?
-
Таки это попытки использовать язык для встроенки на прикладном уровне, имхо.
Вирт порезал его для того, чтобы это всюду влезало, а не чтобы так строки на нём реализовывать :)
-
Таки это попытки использовать язык для встроенки на прикладном уровне, имхо.
Вирт порезал его для того, чтобы это всюду влезало, а не чтобы так строки на нём реализовывать :)
только сам Вирт про это ничего не говорил.. видать не влезло в 16 страниц... хотя вроде место для general purpose language - у него нашлось.. ;)
-
Таки это попытки использовать язык для встроенки на прикладном уровне, имхо.
Вирт порезал его для того, чтобы это всюду влезало, а не чтобы так строки на нём реализовывать :)
Стоп. А как отсутствие массивов с размером неизвестным на этапе компиляции облегчает влезабельность в микроконтроллер?
PS. а чтобы оно везде влезало нужно было отвязываться от 32 бит. А то в msp430 оно, в отличие от С++, до сих пор не влазит.
-
Ну, я так понимаю рантайм упрощается - это раз;
во-вторых, чтобы любой компонент, написанный на языке X, мог быть использован в любом окружении, можно пойти как раз путём сделать невозможным программировать так, что потом конкретный компонент не влезет в конкретное системное окружение (типа, писали на КП библиотеку - внутри понаюзали дин. массивов, потом микроконтроллерщики, допустим, на том же КП захотели использовать эту библиотеку - а оп-па, этот компонент требует кучи не слабого размера и т.п. А запретим дин. массивы - так все будут осмотрительнее по использованию памяти). Возможна такая логика.
Кроме того, Вирт давно смотрел в сторону компиляции с ЯП прямо в схемную топологию, может, из-за этого тоже такие эксперименты с урезанием языка...
-
Ну, я так понимаю рантайм упрощается - это раз;
Если подумать, то нет, не упрощается - менеджер памяти на практике остается тем же самым - размещающим блоки произвольного размера в куче.
Либо нужно сурово усложнить рантайм, сделать его сборку зависящим от сборки основного приложения - усложнить компилятор, чтобы он проводил глубокий анализ кодов приложения и на основе этого генерировал код рантайма. В этом случае да, теоретически возможно скажем уменьшить вероятность фрагментации кучи во время работы.
во-вторых, чтобы любой компонент, написанный на языке X, мог быть использован в любом окружении, можно пойти как раз путём сделать невозможным программировать так, что потом конкретный компонент не влезет в конкретное системное окружение (типа, писали на КП библиотеку - внутри понаюзали дин. массивов, потом микроконтроллерщики, допустим, на том же КП захотели использовать эту библиотеку - а оп-па, этот компонент требует кучи не слабого размера и т.п. А запретим дин. массивы - так все будут осмотрительнее по использованию памяти).
Запрет динамических массивов скорее приведет либо к написанию собственного менеджера памяти и собственных Opaque-массивов, либо к ПЕРЕРАСХОДУ памяти в куче и на стеке. Ибо брать будут с запасом, чтобы гарантированно поместилось.
Возможна такая логика.
Кроме того, Вирт давно смотрел в сторону компиляции с ЯП прямо в схемную топологию, может, из-за этого тоже такие эксперименты с урезанием языка...
А вот на попытку скрестить ежа с ужом действительно немного похоже. Но еще более похоже на еще большее упрощение языка, на подтягивание его еще ближе к Си (С89) - там тоже нет динамических массивов. Правда там и NEW нет, так что следующим логическим шагом для Вирта было бы выпиливание NEW вообще.
Нужно что-то разместить в куче? Делайте через SYSTEM!
PS. А есть еще более странные изменения в Обероне-07/11 относительно Оберона изначального: http://oberspace.dyndns.org/index.php/topic,544.0.html Мы так и не смогли придумать нафига это может быть нужно.
-
Из всего, что сказано, 07 это универсальный язык или нет? Сам Вирт, что пишет по этому поводу? Это дальнейшее развитие языка оберон или узконацеленная ветка языка, для конкретных задач?
-
И такой вопрос, имеет ли смысл запрет дин массивов для программирования микроконтроллёров?
И ещё вопрос, обосновывает ли Вирт, удаление возможностей из языка? Исключил потому, что мол так и так.
-
Из всего, что сказано, 07 это универсальный язык или нет? Сам Вирт, что пишет по этому поводу? Это дальнейшее развитие языка оберон или узконацеленная ветка языка, для конкретных задач?
Судя по репорту - это дальнейшее развитие Оберона. Там нет ни слова про "Oberon-07" - это чисто наше изобретение. Это просто Oberon, новая, уточненная и дополненная ревизия репорта.
-
И такой вопрос, имеет ли смысл запрет дин массивов для программирования микроконтроллёров?
Для программирования микроконтроллеров (и не только) имеет смысл запрет NEW (кучи) вообще. Точнее, по хорошему, NEW вынести бы в либы (тогда был бы один набор либ для скажем микроконтроллеров, другой для десктопа). Но семантика NEW такова, что языковыми методами библиотечно NEW сделать не возможно. Видимо поэтому NEW Вирт до сих пор из языка не выпилил - чтобы NEW стала обычной процедурой пришлось бы существенно усложнить язык.
И ещё вопрос, обосновывает ли Вирт, удаление возможностей из языка? Исключил потому, что мол так и так.
По поднятым тут вопросам обоснований я не видел. Ну и Вирт на письма не отвечает, увы.
-
И вдогонку, в итоге нужна будет одна функция, возвращающая объекты разных типов. Это реализуется с помощью ПРОЦЕДУРНОГО типа?
А на каком языке? Если на обероне, то это делается с помощью наследования записей.
-
Но вот не соображу, как задать СВОЙСТВО данной ЗАПИСИ составного типа (чтобы можно было присвоить, например и ТИП1 и ТИП2)
Классический ООП подход - завести базовый "безтиповый" тип и наследовать от него "типизированные". Везде, где нужна "безтиповость" такскать базовый, а там где нужен тип - кастить к "типизированному". На O7:
TYPE
AnyType = RECORD END;
Type1 = RECORD(AnyType) field: INTEGER END;
Type2 = RECORD(AnyType) field: CHAR END;
PAnyType: POINTER TO AnyType;
PROCEDURE getObject(): PAnyType; (* возвращает любой объект*)
-
Таки это попытки использовать язык для встроенки на прикладном уровне, имхо.
Вирт порезал его для того, чтобы это всюду влезало, а не чтобы так строки на нём реализовывать :)
Да, если бы Вирт объяснил для чего он порезал язык, то разборок было бы сильно меньше... А по поводу применимости в контроллерах я согласен с valexey_u. Добавлю только, что в астробовской версии динамические массивы есть, но только на стеке (неплохой компромисс, по-моему).
-
(типа, писали на КП библиотеку - внутри понаюзали дин. массивов, потом микроконтроллерщики, допустим, на том же КП захотели использовать эту библиотеку - а оп-па, этот компонент требует кучи не слабого размера и т.п. А запретим дин. массивы - так все будут осмотрительнее по использованию памяти). Возможна такая логика.
Ну вот даже на примере из топика видно, что это не так - получившаяся у меня строка неэффективна по памяти и в любой микроконтроллер не засунется...
-
А запретим дин. массивы - так все будут осмотрительнее по использованию памяти).
Хочу отметить еще вот какой момент. В данном примере я приложил определенные усилия (выразившиеся в не совсем тривиальном коде и потраченном времени), чтобы реализация вообще не была полным гавном. В общем случае будет большой соблазн зафигачить связный список CHAR'ов и все (боролись за осмотрительность использования хипа - а получили жуткий оверхед по этому самом хипу). Потому что так проще. И если десктоп такое "простое, но гавно" еще потянет, то для микроконтроллера все равно придется переписывать.
Это я все к идее о том, что простота языка в какой-то момент начинает бить по простоте решений на этом языке. Вплоть до брэйнфака. И есть какой-то оптимум, который может двигаться в одну и другую сторону в зависимости от характера задач. Так вот, по моим текущим ощущениям в случае O7 есть сильное смещение от оптимума (для заявленного general purpose language) в сторону простоты языка. Т.е., решения general purpose задач оказываются неоправданно сложными или неэффективными.
-
vlad
Хочу отметить еще вот какой момент. В данном примере я приложил определенные усилия (выразившиеся в не совсем тривиальном коде и потраченном времени), чтобы реализация вообще не была полным гавном. В общем случае будет большой соблазн зафигачить связный список CHAR'ов и все (боролись за осмотрительность использования хипа - а получили жуткий оверхед по этому самом хипу). Потому что так проще. И если десктоп такое "простое, но гавно" еще потянет, то для микроконтроллера все равно придется переписывать.
Я бы реализовал так. STRING: ARRAY 1024 OF CHAR;
Если вдруг станет мало, увеличу. Уверен для большинства задач, со строками подойдёт.
И останется обычное обращение к массиву s[i]
.
Или сделать через запись.
STRING = RECORD
DATA: ARRAY 1024 OF CHAR;
SIZE: INTEGER; длина строки символов, что бы не проходить весь массив.
END
Но в таком варианте обращение через функцию. Вроде StrIdx(S, 5); Или S.Data[5]
Это я все к идее о том, что простота языка в какой-то момент начинает бить по простоте решений на этом языке. Вплоть до брэйнфака. И есть какой-то оптимум, который может двигаться в одну и другую сторону в зависимости от характера задач. Так вот, по моим текущим ощущениям в случае O7 есть сильное смещение от оптимума (для заявленного general purpose language) в сторону простоты языка. Т.е., решения general purpose задач оказываются неоправданно сложными или неэффективными.
07 переступил эту черту, и гордо шагает. :)
-
В такой реализации перерасход памяти. В исходниках А2, используются строки разных размеров
STRING16
STRING32
Нужно посмотреть реализацию.
-
И такой вопрос, имеет ли смысл запрет дин массивов для программирования микроконтроллёров?
Вообще, для любых приложений 24*365 лучше использовать выделение только фиксированных размеров блоков. Дабы не нарваться на полную фрагментацию памяти. В целом, когда у вас нет кусков непредсказуемого размера, дин. память эффективнее используется.
-
Это я все к идее о том, что простота языка в какой-то момент начинает бить по простоте решений на этом языке. Вплоть до брэйнфака. И есть какой-то оптимум, который может двигаться в одну и другую сторону в зависимости от характера задач. Так вот, по моим текущим ощущениям в случае O7 есть сильное смещение от оптимума (для заявленного general purpose language) в сторону простоты языка. Т.е., решения general purpose задач оказываются неоправданно сложными или неэффективными.
Согласен.
О7 - это культурный, строго типизированный аналог С.
-
Это я все к идее о том, что простота языка в какой-то момент начинает бить по простоте решений на этом языке. Вплоть до брэйнфака. И есть какой-то оптимум, который может двигаться в одну и другую сторону в зависимости от характера задач. Так вот, по моим текущим ощущениям в случае O7 есть сильное смещение от оптимума (для заявленного general purpose language) в сторону простоты языка. Т.е., решения general purpose задач оказываются неоправданно сложными или неэффективными.
Согласен.
О7 - это культурный, строго типизированный аналог С.
Не выйдет сделать аналог Си строго типизированным. Отсюда, собственно, все беды Оберона-07. Банально malloc не сделать. Си вот отлично обходится без массивов переменной длины.
-
Можно сделать так.
Работа со строками происходит, только в статике. Для этого можно сделать массивы разных размеров.
Пример.
TinyString = 16 символов
SmallString = 64 символа
SizeString = 256 символов
HugeString = 1024 символа
В модуле объявляем string := HugeString; И понеслась.
Удобства, одни и те же процедуры для всех типов, плюс умеренный расход памяти.
Дополнительно реализовать тип Text для работы с многомегобайтными строками. Здесь уже идёт деления на чанки, как предлагал Влад.
Разделяем обязанности. Если нужны простые строки выбираем один из стрингов, если, что побольше уже работа с текстом.
-
А вообще если отвлечься. Становится даже грустно. То, что на с++, питоны, джавы, руби делается двумя строками, да те самые контейнеры. На оберон языках нужно, рвать и метать... ???
-
С каждым новым оберон языком под рапортом Вирта, лопата становится меньше, а яму копать глубже. ИМХО.
-
TinyString = 16 символов
SmallString = 64 символа
SizeString = 256 символов
HugeString = 1024 символа
Не, это тупиковый путь. Вот смотри - я пишу компилятор на обероне :) И у меня естественным образом возникает некая сруктурка, в которой я буду держать, например, распарсенный идентификатор переменной. Вот мне какую строку под это дело взять? 16? Не, маловато. 32? Должно хватить. А вдруг тексты на обероне начнут генерить и компилить моим компилятором - фиг знает чего там нагенерят и упрутся в эти 32. Ладно, возьму 1024 - хватит всем (гхм, ну с вероятностью близкой к 1). Но с другой стороны для большинства случаев получаем оверхед в расходе памяти на 2 порядка. Плюс еще пишем в документации (вроде у arkon'а как раз было) - "нельзя использовать идентификаторы больше 1024". Пользователь читает такую доку и недоумевает - а почему, собственно? Че за херня? А! Это потому что в обероне (на котором писался компилятор) нет нормальных строк.
P.S. Кстати реальный случай. В 6-й студии (вроде) было ограничение 255 на длину идентификатора в отладочной информации. Хорошо еще, что это был warning, а не error. Соответственно линковщик мусорил этими варнингами. Ну и понятно, что у них там тоже не было строк, а был где-то char buffer[256];.
P.S.S. Конечно Вирт в своей книжке про компиляторы не гнушается использовать ARRAY 16 OF CHAR для этих самых идентификаторов. Ну так то в учебной книжке - почему бы и нет. Но в промышленном коде - это уже попахивает...
-
Ну, можно ещё задавать размер массива константой.
И для системного уровня не привыкать вводить такие ограничения, типа максимальной длины имени...
Даже твой пример несколько надуманный - если кто-то упрётся в эти 32, переделает генерацию имён. Зачем имена длиннее 64, допустим? "Шоб была возможность"? :)
Вообще, строки - штука хитрая... Очень от задачи зависит.
Можно и вообще делать спец. кучу для строк, где хранить их по ID. Кстати, Губанов, кажется, на такое переходил, когда избавлялся от managed memory в своём софте...
-
А вообще если отвлечься. Становится даже грустно. То, что на с++, питоны, джавы, руби делается двумя строками, да те самые контейнеры. На оберон языках нужно, рвать и метать... ???
В плане того, что О7 - пониженного уровня язык, я говорил, поэтому я не доказываю, что с ним в типовых задачах всё замечательно.
Но хочу заметить, что часто проблемы надумываются, пока не решается конкретная задача. Если сидеть и думать "А как же я буду делать вот это, если оно мне понадобится", то голову сломаешь. А когда есть конкретный контекст, конкретные требования - то и вырабатываются решения. Которые потом иногда обобщаются.
-
Ну, можно ещё задавать размер массива константой.
И для системного уровня не привыкать вводить такие ограничения, типа максимальной длины имени...
Никто не говорит, что константный размер не нужен. Особенно в плане оптимизаций. Пусть будет. Просто действительно много случаев, когда и диапазон большой и оптимизировать нечего.
Посмотри на те же классические БД. Уж насколько тщательно там высчитывают размер колонки CHAR - и все равно расширяют потом. В то же время всегда есть тип TEXT - не такой эффективный как CHAR, но не менее востребованный.
Кстати, Губанов, кажется, на такое переходил, когда избавлялся от managed memory в своём софте...
Да, он боролся с GC. И согласись, у него довольно специфический случай, когда от универсальных строк действительно имеет смысл отказаться.
-
А когда есть конкретный контекст, конкретные требования - то и вырабатываются решения. Которые потом иногда обобщаются.
Ну я и пошел от конкретного контекста - что в компиляторе уже сейчас можно переписать на O7. И сразу уперся в строки, потому что они везде :)
-
Итак, pure Oberon-07 (без динамических массивов). Пытаемся написать модуль для работы со строками любого размера.
Я на оберонах разучился писать, вот идея на псевдоязыке:
struct MemoryBlock
{
virtual size_t size () = 0;
virtual void put (size_t position, char value) = 0;
virtual char get (size_t position) = 0;
};
struct MemoryBlock8b: public MemoryBlock
{
char buffer[8];
size_t size ()
{
return 8;
}
void put (size_t position, char value)
{
this->buffer[position] = value;
}
char get (size_t position)
{
return this->buffer[position];
}
};
struct MemoryBlock16b: public MemoryBlock
{
char buffer[16];
size_t size ()
{
return 16;
}
void put (size_t position, char value)
{
this->buffer[position] = value;
}
char get (size_t position)
{
return this->buffer[position];
}
};
struct MemoryBlock24b: public MemoryBlock
{
char buffer[24];
size_t size ()
{
return 24;
}
void put (size_t position, char value)
{
this->buffer[position] = value;
}
char get (size_t position)
{
return this->buffer[position];
}
};
...и так далее...
MemoryBlock* allocate_memory_block (size_t size)
{
if (size <= 8)
{
return new MemoryBlock8b();
}
if (size <= 16)
{
return new MemoryBlock16b();
}
if (size <= 24)
{
return new MemoryBlock24b();
}
...и так далее...
return 0;
}
-
Вообще, для любых приложений 24*365 лучше использовать выделение только фиксированных размеров блоков. Дабы не нарваться на полную фрагментацию памяти. В целом, когда у вас нет кусков непредсказуемого размера, дин. память эффективнее используется.
Фиксированного размера нехорошо: будет перерасход памяти как на очень мелких так и на очень крупных объектах. Надо выравнивание блока делать прогрессирующим: маленький блок - маленькое выравнивание; а крупнее блок - грубее выравнивание.
Можно и вообще делать спец. кучу для строк, где хранить их по ID. Кстати, Губанов, кажется, на такое переходил, когда избавлялся от managed memory в своём софте...
У меня было два сорта строк: постоянные и временные. Постоянную строку нельзя удалить, зато они внутре суть указатели указывающие на уникальное и неповторимое содержимое (при попытке создать ещё одну строку с уже существующим содержимым возвращается указатель на старое содержимое). Но это не имеет отношения к теме, здесь речь про временную строку: создал, поработал, удалил.
-
А вообще если отвлечься. Становится даже грустно. То, что на с++, питоны, джавы, руби делается двумя строками, да те самые контейнеры. На оберон языках нужно, рвать и метать... ???
В плане того, что О7 - пониженного уровня язык, я говорил, поэтому я не доказываю, что с ним в типовых задачах всё замечательно.
Но хочу заметить, что часто проблемы надумываются, пока не решается конкретная задача. Если сидеть и думать "А как же я буду делать вот это, если оно мне понадобится", то голову сломаешь. А когда есть конкретный контекст, конкретные требования - то и вырабатываются решения. Которые потом иногда обобщаются.
Можно обобщить, но только в динамике. Как сейчас делают в си и паскале, через union или record case(Данные алгоритмы могут быть использованы только в реализуемом проекте). И только потому, что бы не копипастить. Мне кажется это высокоуровневый хак.
-
Стоп. А как отсутствие массивов с размером неизвестным на этапе компиляции облегчает влезабельность в микроконтроллер?
Если почитать всякие рекомендации по написанию программ для встраиваемых устройств, например, промышленный стандарт MISRA C, то там Вы можете обнаружить (обязательное/рекомендационное) требование - не использовать динамическое выделение памяти, а значит прощай и динамические массивы. Оберон не настолько суров, но очевидно сделал шаг в этом направлении.
-
Ну я и пошел от конкретного контекста - что в компиляторе уже сейчас можно переписать на O7. И сразу уперся в строки, потому что они везде :)
Для компилятора, особенно Оберона, не нужны строки с произвольным доступом. Да и учитывая контекст задачи, её можно реализовать с такими накладными расходами по памяти, которые недостижимы при использовании отдельных динамических строк. Просто не нужно пытаться делать обобщённое решение, в данном случае Оберон 07 к этому не располагает.
-
Стоп. А как отсутствие массивов с размером неизвестным на этапе компиляции облегчает влезабельность в микроконтроллер?
Если почитать всякие рекомендации по написанию программ для встраиваемых устройств, например, промышленный стандарт MISRA C, то там Вы можете обнаружить (обязательное/рекомендационное) требование - не использовать динамическое выделение памяти, а значит прощай и динамические массивы. Оберон не настолько суров, но очевидно сделал шаг в этом направлении.
Я это и сам практикую когда под микроконтроллеры пишу - динамическое выделение памяти там вовсе ни к чему. Тем более что в Си прям таки в языке нет средств для этого :-) (есть в стандартной либе).
Но если бы это был шаг в ту сторону, то прежде всего нужно было бы запретить в Обероне NEW вообще (перетащить его в SYSTEM или в другой псевдомодуль). Убирание лишь одной разновидности NEW никак не избавляет от кучи. И не упрощает работу менеджера памяти.
Кроме того, чтобы при этом жилось таки легче, нужно было сделать возможность создания массивов на стеке длиной неизвестной на этапе компиляции. Как это сделано в С99, и как это сделано расширизмом в Astrobe.
То есть этот момент в Обероне сейчас явно как-то не доработан. Повсему выходит, что Оберон (если не менять основы языка) нужно расширять несколькими опциональными стандартными псевдомодулями, вынося туда тот же NEW например. Будет Oberon report и у нему несколько Annex'ов для разных применений.
-
Убирание лишь одной разновидности NEW никак не избавляет от кучи. И не упрощает работу менеджера памяти.
Не могу судить однозначно, зависит от реализации, но по-моему упрощает, ибо все динамически выделенные штуки имеют постоянный размер, их можно проще складировать/замещать друг другом.
-
Убирание лишь одной разновидности NEW никак не избавляет от кучи. И не упрощает работу менеджера памяти.
Не могу судить однозначно, зависит от реализации, но по-моему упрощает, ибо все динамически выделенные штуки имеют постоянный размер, их можно проще складировать/замещать друг другом.
Ну, про это я уже писал: http://oberspace.dyndns.org/index.php/topic,552.msg18360.html#msg18360
Менеджер памяти ничего не знает о множестве типов (и их размеров соответственно) которыми будет оперировать данный конкретный модуль или совокупность модулей. Так что он в любом случае должен быть готов ко всему. Упрощения тут нет.
-
Это всё на уровне кажется. Я и писал, что не могу судить однозначно, но очевидно дополнительное свойство, а насколько оно полезно, можно будет сказать, помахав кувалдой да посозерцав за полученным.
-
Подскажите, пожалуйста, а LEN(v, ind) для массивов, чего возвращает, константную длину массива, или количество заполненных значений? Если второе, то можно наверное было бы использовать для создания строки ARRAY 10, 10, 10 ... OF CHAR? Хотя не знаю, был бы выигрыш тут, или нет
-
Хотя вижу, что возвращает 10, а если как нибудь нулевыми символами забить, или в массивах другой принцип?
-
Для любых массивов, хоть литерных, хоть каких-либо других LEN должна вернуть длину массива, а не строки.
-
Вот что получилось в oberonjs:
https://github.com/vladfolts/oberonjs/wiki/eberon-strings
Критикуйте :)
-
Вот что получилось в oberonjs:
https://github.com/vladfolts/oberonjs/wiki/eberon-strings
Критикуйте :)
Что критиковать? Отличная работа, теперь в языке нормальные строки, которые должны быть.
Как дела обстоят с юникодом? Будет отдельный тип вроде WSTRING, или при компиляции можно выбрать, считать STRING unicod'ом?
Во free pascal'е 2 вариант, есть ключ компиляции, который string заменяет на wstring. Исходники переделывать не нужно. По умолчанию utf8. Как мне кажется, лучше сделать по умолчанию utf32, а уже в зависимости от системы, внутренние функции будут конвертировать в системную кодировку. К примеру в windows utf16, в linux utf8.
-
Как дела обстоят с юникодом?
Ну конкретно в JS реализации деваться особо некуда - строки напрямую отображаются на JS строки. Соответственно юникод поддерживается ровно настолько, насколько он поддерживается исполняющим JS движком.
-
Что, то я сглючил. Код же в js компилируется. Спишем, на недосып.
-
vlad
MODULE test;
IMPORT JS;
VAR
S1: STRING;
S2: STRING;
BEGIN
S1 := "Hello ";
S2 := "World!";
JS.alert(S1 + S2);
END test.
Eberon на STRING ругается.
line 5: undeclared identifier: 'STRING'
Изменения ещё не внесены в язык? Компилю в режиме Eberon.
-
vlad
MODULE test;
IMPORT JS;
VAR
S1: STRING;
S2: STRING;
BEGIN
S1 := "Hello ";
S2 := "World!";
JS.alert(S1 + S2);
END test.
Eberon на STRING ругается.
line 5: undeclared identifier: 'STRING'
Изменения ещё не внесены в язык? Компилю в режиме Eberon.
Онлайн компилятор еще не обновлен, там старая версия. Обновлю завтра.