Oberon space
General Category => Общий раздел => Тема начата: vlad от Октябрь 27, 2013, 06:36:14 am
-
Итак, свершилось: кусочек компилятора был переписан на обероне и скомпилирован самим компилятором. Надо сказать, что в этом что-то есть :) Типа непосредственного прикосновения к вечной проблеме курицы и яйца.
Код приведен в самом конце. Текущие впечатления от кодирования:
- В отсутствие методов запись "Stream.pos(stream)" конечно многословнее и воспринимается хуже, чем "stream.pos()".
- Дублирование в конце имени процедуры - жестокое и беспощадное. Программер должен страдать. Особенно если захочет переименовать. Смысла - 0. Если уж боремся за читабельность, то лучше запретить процедуры длиннее 10 строк...
- Точка с запятой после RETURN. Точнее требование ее отсутствия. Вот уж действительно мелочь, но вот чисто с эргономической точки зрения напрягает страшно. Удобство принесенное в жертву формализму.
- Тяжелый синтаксис. Даже если постараться не смотреть на КАПС. Т.е. я конечно в курсе незабвенного "синтаксического оверхеда", но вот в случае тривиальных процедур - обероновский код выглядит если не многословным, то многобуквенным. И даже "int" vs "INTEGER" начинает напрягать. Т.е. пока пишешь "int" в каком-нибудь С++ это кажется и не таким важным. Но вот пописав "INTGER" начинаешь ценить более короткий вариант.
MODULE Stream;
IMPORT JsString;
TYPE
Type = POINTER TO RECORD
s: JsString.Type;
pos: INTEGER
END;
ReaderProc = PROCEDURE(c: CHAR): BOOLEAN;
PROCEDURE make*(text: JsString.Type): Type;
VAR result: Type;
BEGIN
NEW(result);
result.s := text;
RETURN result
END make;
PROCEDURE eof*(self: Type): BOOLEAN;
RETURN self.pos = JsString.len(self.s)
END eof;
PROCEDURE pos*(self: Type): INTEGER;
RETURN self.pos
END pos;
PROCEDURE setPos*(self: Type; pos: INTEGER);
BEGIN
ASSERT(pos <= JsString.len(self.s));
self.pos := pos
END setPos;
PROCEDURE next*(self: Type; n: INTEGER);
BEGIN
ASSERT(self.pos + n <= JsString.len(self.s));
self.pos := self.pos + n;
END next;
PROCEDURE peekChar*(self: Type): CHAR;
BEGIN
ASSERT(~eof(self));
RETURN JsString.at(self.s, self.pos)
END peekChar;
PROCEDURE getChar*(self: Type): CHAR;
VAR result: CHAR;
BEGIN
ASSERT(~eof(self));
result := JsString.at(self.s, self.pos);
INC(self.pos);
RETURN result
END getChar;
PROCEDURE peekStr*(self: Type; len: INTEGER): JsString.Type;
VAR max: INTEGER;
BEGIN
max := JsString.len(self.s) - self.pos;
IF len > max THEN
len := max;
END
RETURN JsString.substr(self.s, self.pos, len)
END peekStr;
PROCEDURE read*(self: Type; f: ReaderProc): BOOLEAN;
BEGIN
WHILE ~eof(self) & f(peekChar(self)) DO
next(self, 1);
END
RETURN ~eof(self)
END read;
PROCEDURE lineNumber*(self: Type): INTEGER;
VAR
line: INTEGER;
lastPos: INTEGER;
BEGIN
lastPos := JsString.indexOf(self.s, 0DX);
WHILE (lastPos # -1) & (lastPos < self.pos) DO
INC(line);
lastPos := JsString.indexOfFrom(self.s, 0DX, lastPos + 1);
END;
RETURN line + 1
END lineNumber;
END Stream.
-
За много лет творческого пиздежа против оберона первые строчки на обероне, хо-хо-хо.
-
За много лет творческого пиздежа против оберона первые строчки на обероне, хо-хо-хо.
Ну а ты обероном до сих пор не пользуешься, ва-ха-ха.
-
Да и тут Ъ"обероном" не пахнет, всего лишь "-07".
С твоей дебильной точки зрения, С++11-лохи не могут в С++'99.
-
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
-
Да и тут Ъ"обероном" не пахнет, всего лишь "-07".
С твоей дебильной точки зрения, С++11-лохи не могут в С++'99.
С++11 отличается от С++99 куда меньше, чем КП от Оберона.
Если уж сравнивать, то КП -- это аналог С++83, а Оберон -- это аналог С70...
Учи матчасть.
-
Учи матчасть.
Иди нахуй, чучело.
В твоём первом высере ничего не было про степени отличия. На хабре такую хуйню втирай.
-
Component Pascal is Oberon microsystems' refinement of the Oberon-2 language. Oberon microsystems thanks H. Mössenböck and N. Wirth for the friendly permission to use their Oberon2 report as basis for this document.
Вот именно что КП -- это версия Oberon-2 language, а вовсе не виртовского Оберона! Говорю же -- учи матчасть!
-
Текущие впечатления от кодирования:
- конечно многословнее и воспринимается хуже
- жестокое и беспощадное. страдать. Если уж, то лучше...
- мелочь, но вот чисто с эргономической точки зрения напрягает страшно. Удобство принесенное в жертву формализму.
- Тяжелый синтаксис. обероновский код выглядит если не многословным, то многобуквенным. начинает напрягать. пописав "INTGER" начинаешь ценить более короткий вариант.
Эмоциональные эксперты на страже всего мейнстримного. Одни несчётные параметры. Ну этому аналезу от прафессеаналав можна вереть, дыа.
-
Оберон-2 был разработан в 1991 году в Швейцарской высшей технической школе (Цюрих) Никлаусом Виртом и Ханспетером Мёссенбёком
http://www.uni-vologda.ac.ru/oberon/o2rus.htm
Всё в порядке, гони хуйню дальше, женя. Ты же вон какой профессионал ;)
-
А как вообще этот код может компилироваться?
MODULE Stream;
IMPORT JsString;
TYPE
Type = POINTER TO RECORD
s: JsString.Type;
pos: INTEGER
END;
...
PROCEDURE make*(text: JsString.Type): Type;
...
Как можно экспортировать процедуру make, например, если тип Type, входящий в сигнатуру этой процедуры, неэкспортирован? о_О
- В отсутствие методов запись "Stream.pos(stream)" конечно многословнее и воспринимается хуже, чем "stream.pos()".
Это же не oberon-way. По-обероновски было бы сделать фабрику, которая кроме выделения необходимой для объекта памяти ещё и проинициализирует его методы-процедурные переменные. И тогда будет вполне привычная запись "stream.pos()"...
-
Оберон-2 был разработан в 1991 году в Швейцарской высшей технической школе (Цюрих) Никлаусом Виртом и Ханспетером Мёссенбёком
http://www.uni-vologda.ac.ru/oberon/o2rus.htm
Мало ли что Свердлов понаписал на своём сайте. Он и про арифметику синтаксиса пургу гнал.
Всё в порядке, гони хуйню дальше, женя. Ты же вон какой профессионал ;)
петрушка, слушай лучше Стравинского, больше пользы принесёшь...
-
- В отсутствие методов запись "Stream.pos(stream)" конечно многословнее и воспринимается хуже, чем "stream.pos()".
Это же не oberon-way. По-обероновски было бы сделать фабрику, которая кроме выделения необходимой для объекта памяти ещё и проинициализирует его методы-процедурные переменные. И тогда будет вполне привычная запись "stream.pos()"...
Хотя нет, выйдет примерно так: "stream.pos(stream)". Да уж, хрен редьки не слаще...
-
Да и тут Ъ"обероном" не пахнет, всего лишь "-07".
Кчтати, Влад, если доведёшь компилятор до соответствия этому рапорту, можно его смело переименовывать из Oberon-07/11 в Oberon-13, или же просто в Oberon. Так будет правильнее...
-
Оберон-2 был разработан в 1991 году в Швейцарской высшей технической школе (Цюрих) Никлаусом Виртом и Ханспетером Мёссенбёком
http://www.uni-vologda.ac.ru/oberon/o2rus.htm
Мало ли что Свердлов понаписал на своём сайте. Он и про арифметику синтаксиса пургу гнал.
Всё в порядке, гони хуйню дальше, женя. Ты же вон какой профессионал ;)
петрушка, слушай лучше Стравинского, больше пользы принесёшь...
Заявка на победу. То есть ты сомневаешься, что О2 был написан виртом, как развитие Оберона? Ну ты и ебанько.
-
Оберон-2 был разработан в 1991 году в Швейцарской высшей технической школе (Цюрих) Никлаусом Виртом и Ханспетером Мёссенбёком
http://www.uni-vologda.ac.ru/oberon/o2rus.htm
Мало ли что Свердлов понаписал на своём сайте. Он и про арифметику синтаксиса пургу гнал.
Всё в порядке, гони хуйню дальше, женя. Ты же вон какой профессионал ;)
петрушка, слушай лучше Стравинского, больше пользы принесёшь...
Заявка на победу. То есть ты сомневаешься, что О2 был написан виртом, как развитие Оберона? Ну ты и ебанько.
Третий раз повторяю (а значит это истина) -- учи матчасть!
Мёссенбёк для своих нужд переделал Оберон, обозвал его Обероном-2, а Вирт подмахнул рапорт об Обероне-2. Вот и всё участие Вирта в создании Оберона-2.
-
А ты рядом стоял, свечку держал. Вот оно что.
Какой ты. :)
-
Сколько раз я тебя назвал уёбищем? Два, три? Может уже пора закрепить результат?
-
Vlad, у меня в Делфи код ещё более многословен. Это абсолютно не напрягает, если настроить такие редакторы как Notepad++ (плагин QuickText) или Sublime Text 2. Практически для всего пишешь сниппеты или их аналоги. И целые блоки и серии описаний в несколько нажатий клавиш. Например, i CTRL + ENTER = INTEGER. r =END RETURN x; Попробуйте.
-
Дельфи это ведь моветон. Грешно вспоминать такое на форуме умных адекватных людей ;) Вот вы бы лучше вспомнили Луа, или Скала.
-
Кчтати, Влад, если доведёшь компилятор до соответствия этому рапорту, можно его смело переименовывать из Oberon-07/11 в Oberon-13, или же просто в Oberon. Так будет правильнее...
Файл репорта Вирт назвал Oberon07.Report.pdf - 07, а не 13
-
Хотя нет, выйдет примерно так: "stream.pos(stream)". Да уж, хрен редьки не слаще...
Угу. Без расширений (методы или не знаю что) все равно ерунда будет.
-
Vlad, у меня в Делфи код ещё более многословен. Это абсолютно не напрягает, если настроить такие редакторы как Notepad++ (плагин QuickText) или Sublime Text 2.
Кстати, а нет ли у тебя расцветки для Сублима?
-
Гм. А что мешает сделать как-то так?
MODULE Test;
IMPORT SomeModule;
PROCEDURE LetDoIt;
VAR
var : SomeModule.SomeType;
process : PROCEDURE (v : SomeModule.SomeType);
BEGIN
process := SomeModule.ProcessSomeType;
process(var);
(* и тут еще 100500 строк кода с испоользованием process *)
END LetDoIt;
END Test;
-
Гм. А что мешает сделать как-то так?
MODULE Test;
IMPORT SomeModule;
PROCEDURE LetDoIt;
VAR
var : SomeModule.SomeType;
process : PROCEDURE (v : SomeModule.SomeType);
BEGIN
process := SomeModule.ProcessSomeType;
process(var);
(* и тут еще 100500 строк кода с испоользованием process *)
END LetDoIt;
END Test;
Так ещё хуже -- повышается уровень косвенности, труднее понимать, что там вообще происходит...
-
Кстати, а нет ли у тебя расцветки для Сублима?
К сожалению, нет. Нужно брать за основу пакет Pascal. Там 5,35 кибибайта размер всего.
-
Гм. А что мешает сделать как-то так?
MODULE Test;
IMPORT SomeModule;
PROCEDURE LetDoIt;
VAR
var : SomeModule.SomeType;
process : PROCEDURE (v : SomeModule.SomeType);
BEGIN
process := SomeModule.ProcessSomeType;
process(var);
(* и тут еще 100500 строк кода с испоользованием process *)
END LetDoIt;
END Test;
Так ещё хуже -- повышается уровень косвенности, труднее понимать, что там вообще происходит...
foo.bar() -- тоже уровень косвенности огого, особенно если bar это виртуальная функция - ведь ты даже не знаешь в каком модуле она лежит! И не узнаешь пока все исходники не перетрясешь!
А тут же - все локально и явным образом определено.
-
А как вообще этот код может компилироваться?
Это бага. Пока прокатывает, потому что модуль потребляется из жабаскрипта (а не из других обероновских модулей).
-
Это бага. Пока прокатывает, потому что модуль потребляется из жабаскрипта (а не из других обероновских модулей).
Я тут было уже фикснул, но подумал, что нет, не бага. Например вот такой случай:
MODULE test;
TYPE
T = POINTER TO RECORD END;
Proc* = PROCEDURE(): T;
PROCEDURE method1*(): T;
...
PROCEDURE method2*(): T;
...
PROCEDURE do*(method: Proc);
...
IMPORT test;
...
test.do(test.p1);
Возможно даже Proc не надо экспортировать. Так что пока откачу фикс.
-
Это бага. Пока прокатывает, потому что модуль потребляется из жабаскрипта (а не из других обероновских модулей).
Я тут было уже фикснул, но подумал, что нет, не бага. Например вот такой случай:
MODULE test;
TYPE
T = POINTER TO RECORD END;
Proc* = PROCEDURE(): T;
PROCEDURE method1*(): T;
...
PROCEDURE method2*(): T;
...
PROCEDURE do*(method: Proc);
...
IMPORT test;
...
test.do(test.p1);
Всё же я думаю, что это баг. Как можно в обероне использовать процедуры с неизвестными сигнатурами?
Возможно даже Proc не надо экспортировать. Так что пока откачу фикс.
-
Переписал лексер на оберон:
https://github.com/vladfolts/oberonjs/blob/master/src/oberon/Lexer.ob
Свежие впечатления:
- Оказывается операции сравнения в обероне имеют наименьший приоритет. Это приводит к тому, что все нетривиальные условия сразу обрастают скобками:
if (a != b && a != c)
===
IF (a # b) & (a # c) THEN
- Условия очень быстро становятся сложными из-за одного RETURN
- Ну и апофеоз всего: Цикл Дейкстры. Отгадайте что он делает и есть ли в нем ошибка:
PROCEDURE isReservedWorld(s: JsString.Type; words: JsString.Type): BOOLEAN;
VAR
i, w: INTEGER;
BEGIN
WHILE (w < JsString.len(words))
& (i < JsString.len(s))
& (JsString.at(words, w) = JsString.at(s, i))
& ((i # 0) OR (w = 0) OR (JsString.at(words, w - 1) = " ")) DO
INC(w);
INC(i);
ELSIF (w < JsString.len(words))
& ((i < JsString.len(s)) OR (JsString.at(words, w) # " ")) DO
INC(w);
i := 0;
END;
RETURN i = JsString.len(s)
END isReservedWorld;
-
Переписал лексер на оберон:
https://github.com/vladfolts/oberonjs/blob/master/src/oberon/Lexer.ob
Свежие впечатления:
- Оказывается операции сравнения в обероне имеют наименьший приоритет. Это приводит к тому, что все нетривиальные условия сразу обрастают скобками:
if (a != b && a != c)
===
IF (a # b) & (a # c) THEN
Ну, в данном случае проблема в том, что у операций сравнения одинаковый приоритет.
С другой стороны, я и в сях/сишарпе часто сложные логические выражения в скобки оборачиваю -- в основном для группирования частей выражений, иногда лень вспоминать приоритеты. Сомневаешься в приоритетах -- оберни в скобки!
- Ну и апофеоз всего: Цикл Дейкстры. Отгадайте что он делает и есть ли в нем ошибка:
PROCEDURE isReservedWorld(s: JsString.Type; words: JsString.Type): BOOLEAN;
VAR
i, w: INTEGER;
BEGIN
WHILE (w < JsString.len(words))
& (i < JsString.len(s))
& (JsString.at(words, w) = JsString.at(s, i))
& ((i # 0) OR (w = 0) OR (JsString.at(words, w - 1) = " ")) DO
INC(w);
INC(i);
ELSIF (w < JsString.len(words))
& ((i < JsString.len(s)) OR (JsString.at(words, w) # " ")) DO
INC(w);
i := 0;
END;
RETURN i = JsString.len(s)
END isReservedWorld;
Цикл, конечно, жестокий. А всё из-за неверных структур данных типа:
reservedWords := JsString.make(
"ARRAY IMPORT THEN BEGIN IN TO BY IS TRUE CASE MOD TYPE CONST MODULE UNTIL DIV NIL VAR DO OF WHILE ELSE OR ELSIF POINTER END PROCEDURE FALSE RECORD FOR REPEAT IF RETURN");
jsReservedWords := JsString.make(
"break case catch continue debugger default delete do else finally for function if in instanceof new return switch this throw try typeof var void while with Math"); (* Math is used in generated code for some functions so it is reserved word from code generator standpoint *)
ЗЫ. Вот, кстати, яркий пример, что мало кто читает руководства по стилю кодирования (http://oberspace.dyndns.org/index.php/topic,535.0.html). Какой смысл их писать?.. :'(
-
С самим циклом не разбирался, но попутно вопрос возник, разве там w не надо присваивать перед циклом?
-
С самим циклом не разбирался, но попутно вопрос возник, разве там w не надо присваивать перед циклом?
Да и i тоже :-)
-
С самим циклом не разбирался, но попутно вопрос возник, разве там w не надо присваивать перед циклом?
С самим циклом не разбирался, но попутно вопрос возник, разве там w не надо присваивать перед циклом?
Да и i тоже :-)
Там же в компиляторе сделано, что всем переменным по умолчанию присваиваются нулевые значения -- числам нули, указателям nil...
-
С самим циклом не разбирался, но попутно вопрос возник, разве там w не надо присваивать перед циклом?
Да и i тоже :-)
Тоже не надо, или тоже надо :) ?
-
С самим циклом не разбирался, но попутно вопрос возник, разве там w не надо присваивать перед циклом?
С самим циклом не разбирался, но попутно вопрос возник, разве там w не надо присваивать перед циклом?
Да и i тоже :-)
Там же в компиляторе сделано, что всем переменным по умолчанию присваиваются нулевые значения -- числам нули, указателям nil...
Какая разница как именно в конкретной реализации это сделано? Язык не гарантирует, следовательно будет UB.
-
Там же в компиляторе сделано, что всем переменным по умолчанию присваиваются нулевые значения -- числам нули, указателям nil...
Это исходя из сообщения о языке сделано? Или просто так удобнее?
-
Там же в компиляторе сделано, что всем переменным по умолчанию присваиваются нулевые значения -- числам нули, указателям nil...
Это исходя из сообщения о языке сделано? Или просто так удобнее?
В сообщении о языке я на эту тему ничего не нашёл -- там вообще нет ни одного вхождения слова "default".
Похоже, судя по сообщению эти значения не поределены (undefined)...
-
Ну, в данном случае проблема в том, что у операций сравнения одинаковый приоритет.
Не одинаковый, а наименьший - я ж написал :)
С другой стороны, я и в сях/сишарпе часто сложные логические выражения в скобки оборачиваю -- в основном для группирования частей выражений, иногда лень вспоминать приоритеты. Сомневаешься в приоритетах -- оберни в скобки!
Сложные - да. Простые типа "p && p->f()" обычно не оборачивают.
Цикл, конечно, жестокий. А всё из-за неверных структур данных типа:
Согласен. В оригинале на жабскрипте это был просто поиск в массиве ключевых слов. Который я ни разу не отлаживал. В обероне это превратилось в строчку с пробелами, потому что нет нормальной инициализации массивов и нет динамических массивов (даже если была бы инициализация, то пришлось бы делать все ключевые слова одинакового размера, забивая окончания нулями). Это еще как пример того, когда простота языка вылезает боком в решении на этом языке. Вообще написание Цикла Дейкстры чем-то напоминает решение кроссворда или головоломки - такая разминка для ума. Не знаю сколько их нужно написать, чтоб потом говорить о выписывании "на автомате" вот этих трехэтажных условий.
ЗЫ. Вот, кстати, яркий пример, что мало кто читает руководства по стилю кодирования (http://oberspace.dyndns.org/index.php/topic,535.0.html). Какой смысл их писать?.. :'(
Я читал их в свое время. Они мне показались полностью согласующимися с моим стилем. Поэтому я даже на заметку не стал брать. Что не так?
-
В сообщении о языке я на эту тему ничего не нашёл -- там вообще нет ни одного вхождения слова "default".
Похоже, судя по сообщению эти значения не определены (undefined)...
В репорте сказано, что значения локальных переменных на входе в процедуру не определены (при этом, конечно, они могут иметь и нулевые значения). Т.е., да - по репорту это чистой воды UB .
С другой стороны, лично я считаю этот момент принципиальным, поэтому в своей реализации я сделал уточнение (https://github.com/vladfolts/oberonjs/wiki/Original-report-refinements#2-variables-default-values).
В ББ этот момент тоже считают принципиальным (герметичность типов, не хухры-мухры), но не до конца - там обнуляют только указатели. Их можно понять - 90-е годы, Си все еще популярен и там неопределенные переменные в порядке вещей.
-
Ну так что, правильно цикл работает?
-
Ну так что, правильно цикл работает?
Тесты проходят. Но он страшный. Поэтому уверенности нет. Хотелось одобрения экспертов.
-
Надо бы состояние добавить для упрощения условий.
Типа так ( обероном не проверял :) )
PROCEDURE isReservedWorld(s: JsString.Type; words: JsString.Type): BOOLEAN;
VAR
i, w: INTEGER;
ok: BOOLEAN;
i := 0 ;
w := 0 ;
ok := TRUE ;
BEGIN
WHILE ok
& (w < JsString.len(words))
& (i < JsString.len(s))
& (JsString.at(words, w) = JsString.at(s, i)) DO
INC(w);
INC(i);
ELSIF (w < JsString.len(words))
& (JsString.at(words, w) # " ")) DO
INC(w);
i := 0;
ok := FALSE;
ELSIF (w < JsString.len(words))
& (i < JsString.len(s)) DO
INC(w);
i := 0;
ok := TRUE;
END;
RETURN (i = JsString.len(s)) & ok
END isReservedWorld;
-
PS.
RETURN ok
-
PPS.
тьфу, вот так, ( & ok можно выкинуть)
RETURN (i = JsString.len(s))
-
Тесты проходят. Но он страшный. Поэтому уверенности нет. Хотелось одобрения экспертов.
Не знаю, как там эксперты, а я не одобряю.
И метод разработки (метод тыка) и результат - ужасны.
Как при таком методе получилось нечто работоспособное - для меня большая загадка.
Вот что дает осмысленное проектирование.
Кому надо, перепишет с C# на смесь JS и Оберона:
static bool isReservedWorld(string s, string words) {
int i = 0, w = 0;
bool prevEq = true;
while (true) {
if (prevEq && (w < words.Length) && (i < s.Length) && (words[w] == s[i])) {
w++;
i++;
} else if (prevEq && (w < words.Length) && (i < s.Length) && /*это избыточно*/ (words[w] != s[i])) {
prevEq = false;
i = 0;
} else if (w < words.Length && !prevEq) {
if (words[w] == ' ') {
prevEq = true;
}
w++;
} else {
break;
}
}
return i == s.Length;
}
По поводу цикла Дейкстры - два наблюдения. Не помню, были ли они раньше.
1. Во многих случаях ЦД - это смешение разных уровней абстракций. И это плохо.
2. Во много благодаря п.1 ЦД весьма похож на реализацию конечного автомата. Только состояния здесь обычно не вводятся явно, а вычисляются из переменных на каждом шаге. Отсюда - неэффективность. Тоже плохо.
p.s. забыл отметить, что ключевые слова должны быть разделены ровно одним пробелом.
-
2 Peter.
В ситуации, когда
prevEq && (w < words.Length) && (i = s.Length)
но words[w] не пробел , то ошибка
-
Ага, прощёлкал. Сосредоточился на цикле, а возврат оставил как в оригинале.
Надо заменить:
return i == s.Length;
на
return i == s.Length && (w == words.Length || words[w] == ' ');
-
2 Peter
Нет, здесь в цикле недочёт. Уходит преждевременно на break, при тех условиях, что я указал.
-
PS.
К примеру ищем в строке слово "ON".
Наткнулись на "ONE" и вышли, а далее в строке может всё же есть "ON"
-
PPS.
Конечно, если слова в строке отсортированы, то проблем в цикле нет.
-
Я считал, что отсортированы.
Иначе на каждый чих - полный проход по всем словам.
Алгоритм и без того дурацкий.
-
WHILE (i < JsString.len(s)) & (w < JsString.len(words)) DO
WHILE (w < JsString.len(words)) & (i < JsString.len(s)) & (JsString.at(words, w) = JsString.at(s, i)) DO
INC(w); INC(i)
END
IF i < JsString.len(s) THEN
i := 0;
WHILE (w < JsString.len(words)) & (JsString.at(words, w) # " ") DO
w := w + 1
END;
WHILE (w < JsString.len(words))& (JsString.at(words, w) = " ") DO
w := w + 1
END
END
END;
RETURN i = JsString.len(s)
-
Тесты проходят. Но он страшный. Поэтому уверенности нет. Хотелось одобрения экспертов.
Не знаю, как там эксперты, а я не одобряю.
Да, я вот как раз ждал твоего веского слова :)
И метод разработки (метод тыка) и результат - ужасны.
Дык, ошибка-то есть? А то не очень просто делать дифф с твоим шарповым вариантом. При том, что он в той же степени нечитаем (при том, что я не ставлю под сомнение осмысленное проектирование).
Как при таком методе получилось нечто работоспособное - для меня большая загадка.
Тесты спасают. Когда нужно получить что-то работоспособное, но непонятно как.
-
PS.
К примеру ищем в строке слово "ON".
Наткнулись на "ONE" и вышли, а далее в строке может всё же есть "ON"
Кстати, именно эту ошибку мне пришлось исправить в процессе отладки моего цикла.
-
Кстати, именно эту ошибку мне пришлось исправить в процессе отладки моего цикла.
там есть IF и ELSIF
-
Дык, ошибка-то есть?
Во всяком случае, я не в состоянии ее найти :)
Хотя, все зависит от того, что считать ошибкой...
-
Если не зацикливаться на ЦД :)
Рефакторинг исходного кода с такими ограничениями на строку слов:
слова отсортированы,
между словами один пробел,
после последнего слова тоже стоит пробел
PROCEDURE isReservedWorld(s: JsString.Type; words: JsString.Type): BOOLEAN;
VAR
i, w, w0: INTEGER;
BEGIN
i := 0 ;
w := 0 ;
w0:= 0 ;
WHILE (w < JsString.len(words))
& ((i < JsString.len(s)) OR (JsString.at(words, w) # " ")) DO
IF (i < JsString.len(s)) & (JsString.at(words, w0) = JsString.at(s, i)) THEN
INC(w); w0 := w;
INC(i);
ELSIF (JsString.at(words, w) # " ") THEN
INC(w);
ELSE
INC(w); w0 := w;
i := 0;
END;
END;
RETURN (w < JsString.len(words))
END isReservedWorld;
PS.
проверять лень :)
-
описка:
в условии WHILE
не
... OR (JsString.at(words, w) # " ")) DO
а
... OR (JsString.at(words, w0) # " ")) DO
PS.
Как же тяжко писать много букафф, вместо просто $d(words(s)) как в МУМПСе :)
-
после последнего слова тоже стоит пробел
Кстати да, если добавить с начала и в конце пробелы - то условия упрощаются (не нужны проверки на пограничные условия - начало и конец строки).
-
PS.
Как же тяжко писать много букафф, вместо просто $d(words(s)) как в МУМПСе :)
Хе-хе. На питоне:
s in ' '.split(words)
Типа:
- Почему в популярны языках нет Цикла Дейкстры?
- Потому что он там нинужен
:)
-
Да не ниненужен, а не осилили :)
-
Решение мне не понятно. Мало узнать, является слово ключевым или нет, нужно знать какое это ключевое слово. Повторно сравнивать строки в дальнейшем будет излишеством. А если так, то таблица вместе с линейным поиском по ней, немногим лучше таблицы, встроенной в развернутый линейный поиск, которые легко реализуется на Обероне.
А вообще-то тут должна быть расстановочная таблица (хэш-массив)
-
PS.
Как же тяжко писать много букафф, вместо просто $d(words(s)) как в МУМПСе :)
Хе-хе. На питоне:
s in ' '.split(words)
И в хацкеле:
s `elem` (words keywords)
(words -- это в хаскелле стандартная функция, поэтому строку, содержащую ключевые слова, назовём как keywords)
Типа:
- Почему в популярны языках нет Цикла Дейкстры?
- Потому что он там нинужен
:)
Либо легко эмулируется при необходимости (как в сях тремя макросами (http://oberspace.dyndns.org/index.php/topic,39.msg1153.html#msg1153)).
#define WHILE for(;;) if (
#define DO ) {
#define ELSIF ; } else if (
#define WEND ; } else break
int gcd(int x, int y)
{
WHILE x > y DO x = x - y
ELSIF x < y DO y = y - x
WEND;
return x;
}
-
Решение мне не понятно. Мало узнать, является слово ключевым или нет, нужно знать какое это ключевое слово.
Тут лексер не совсем классический. Он не просто разбирает входной поток на лексемы, а ожидает вполне конкретную лексему. В данном случае он ожидает идентификатор и ничто другое. Поэтому для того, чтобы определить удалось ли ему получить идентификатор или нет - ему не обязательно знать какое именно ключевое слово ему попалось вместо идентификатора. Повторного сравнения все равно не будет.
Повторно сравнивать строки в дальнейшем будет излишеством. А если так, то таблица вместе с линейным поиском по ней, немногим лучше таблицы, встроенной в развернутый линейный поиск, которые легко реализуется на Обероне.
А вообще-то тут должна быть расстановочная таблица (хэш-массив)
В данном случае нужно то, что нужно - определить принадлежность к списку зарезервированных слов (там на самом деле два этих списка, другой список - список жабаскриптовых зарезервированных слов). Как можно проще. Альтернативные решения приветствуются, но в рамках поставленной задачи.
-
PROCEDURE IsReservedOberonWord(s: ARRAY OF CHAR): BOOLEAN;
BEGIN
RETURN s = "ARRAY" OR s = "IMPORT" OR s = "THEN" OR s = "BEGIN" OR s = "IN" OR s = "TO" OR s = "BY" OR
s = "IS" OR s = "TRUE" OR s = "CASE" OR s = "MOD" OR s = "TYPE" OR s = "CONST" OR s = "MODULE" OR
s = "UNTIL" OR s = "DIV" OR s = "NIL" OR s = "VAR" OR s = "DO" OR s = "OF" OR s = "WHILE" OR s = "ELSE" OR
s = "OR" OR s = "ELSIF" OR s = "POINTER" OR s = "END" OR s = "PROCEDURE" OR s = "FALSE" OR
s = "RECORD" OR s = "FOR" OR s = "REPEAT" OR s = "IF" OR s = "RETURN"
END IsReservedOberonWord;
Проще некуда. "Таблица" зарезервированных слов немного разбавлена кодом, но в в целом простота составления приблизительно на том же уровне. Не тестировал.
-
Проще некуда.
Да, засчитано. Но не могу применить в текущей реализации - оно странслируется в ужос-ужос.
-
RETURN s = "ARRAY" OR s = "IMPORT" OR s = "THEN" OR s = "BEGIN" OR s = "IN" OR s = "TO" OR s = "BY"
Гы! И здесь тоже скобочки нужны! :)
-
Проще некуда.
Да, засчитано. Но не могу применить в текущей реализации - оно странслируется в ужос-ужос.
Дак, этта.. Пофиксить надыть. Хотя вначале с этими relations надо разобраться.
-
RETURN s = "ARRAY" OR s = "IMPORT" OR s = "THEN" OR s = "BEGIN" OR s = "IN" OR s = "TO" OR s = "BY"
Гы! И здесь тоже скобочки нужны! :)
Проглядел, хотя так:
RETURN (s = "ARRAY") OR (s = "IMPORT") OR (s = "THEN") OR (s = "BEGIN") OR (s = "IN") OR (s = "TO") OR (s = "BY")
по-моему, читаемость только выше
-
Да уж, уныло, конечно:
PROCEDURE IsReservedOberonWord(s: ARRAY OF CHAR): BOOLEAN;
BEGIN
RETURN (s = "ARRAY") OR (s = "IMPORT") OR (s = "THEN")
OR (s = "BEGIN") OR (s = "IN") OR (s = "TO")
OR (s = "BY") OR (s = "IS") OR (s = "TRUE")
OR (s = "CASE") OR (s = "MOD") OR (s = "TYPE")
OR (s = "CONST") OR (s = "MODULE") OR (s = "UNTIL")
OR (s = "DIV") OR (s = "NIL") OR (s = "VAR")
OR (s = "DO") OR (s = "OF") OR (s = "WHILE")
OR (s = "ELSE") OR (s = "OR") OR (s = "ELSIF")
OR (s = "POINTER") OR (s = "END") OR (s = "PROCEDURE")
OR (s = "FALSE") OR (s = "RECORD") OR (s = "FOR")
OR (s = "REPEAT") OR (s = "IF") OR (s = "RETURN")
END IsReservedOberonWord;
-
А вот вариант, основанный на идее, подсмотренной в исходниках BB:
PROCEDURE IsReservedOberonWord(s: ARRAY OF CHAR): BOOLEAN;
VAR res: BOOLEAN
BEGIN
CASE s[0] OF
| 'A': res := s = "ARRAY";
| 'B': res := (s = "BEGIN") OR (s = "BY");
| 'C': res := (s = "CASE") OR (s = "CONST");
| 'D': res := (s = "DIV") OR (s = "DO");
| 'E': res := (s = "END") OR (s = "ELSE") OR (s = "ELSIF");
| 'F': res := (s = "FALSE") OR (s = "FOR");
| 'I': res := (s = "IMPORT") OR (s = "IN") OR (s = "IS") OR (s = "IF");
| 'M': res := (s = "MOD") OR (s = "MODULE");
| 'N': res := s = "NIL";
| 'O': res := (s = "OF") OR (s = "OR");
| 'P': res := (s = "POINTER") OR (s = "PROCEDURE");
| 'R': res := (s = "RECORD") OR (s = "REPEAT") OR (s = "RETURN");
| 'T': res := (s = "TO") OR (s = "THEN") OR (s = "TRUE") OR (s = "TYPE");
| 'U': res := s = "UNTIL";
| 'V': res := s = "VAR";
| 'W': res := s = "WHILE";
ELSE res := FALSE
END;
RETURN res
END IsReservedOberonWord;
-
А вот вариант, основанный на идее, подсмотренной в исходниках BB:
PROCEDURE IsReservedOberonWord(s: ARRAY OF CHAR): BOOLEAN;
VAR res: BOOLEAN
BEGIN
CASE s[0] OF
| 'A': res := s = "ARRAY";
| 'B': res := (s = "BEGIN") OR (s = "BY");
| 'C': res := (s = "CASE") OR (s = "CONST");
| 'D': res := (s = "DIV") OR (s = "DO");
| 'E': res := (s = "END") OR (s = "ELSE") OR (s = "ELSIF");
| 'F': res := (s = "FALSE") OR (s = "FOR");
| 'I': res := (s = "IMPORT") OR (s = "IN") OR (s = "IS") OR (s = "IF");
| 'M': res := (s = "MOD") OR (s = "MODULE");
| 'N': res := s = "NIL";
| 'O': res := (s = "OF") OR (s = "OR");
| 'P': res := (s = "POINTER") OR (s = "PROCEDURE");
| 'R': res := (s = "RECORD") OR (s = "REPEAT") OR (s = "RETURN");
| 'T': res := (s = "TO") OR (s = "THEN") OR (s = "TRUE") OR (s = "TYPE");
| 'U': res := s = "UNTIL";
| 'V': res := s = "VAR";
| 'W': res := s = "WHILE";
ELSE res := FALSE
END;
RETURN res
END IsReservedOberonWord;
Тогда уж можно и полное дерево построить :-)
-
Тогда уж можно и полное дерево построить :-)
Это будет уже перебор.
В предложенном мной варианте компилятор сгенерирует по кэйсу очень быструю таблицу переходов, и далее потребуется не более четырёх сравнений строк.
Если, например, повторить этот трюк внутри обработки случаев 'I' и 'T', то профит будет уже не столь впечатляющим, если вообще будет.
-
А вот вариант, основанный на идее, подсмотренной в исходниках BB:
PROCEDURE IsReservedOberonWord(s: ARRAY OF CHAR): BOOLEAN;
VAR res: BOOLEAN
BEGIN
CASE s[0] OF
| 'A': res := s = "ARRAY";
| 'B': res := (s = "BEGIN") OR (s = "BY");
| 'C': res := (s = "CASE") OR (s = "CONST");
| 'D': res := (s = "DIV") OR (s = "DO");
| 'E': res := (s = "END") OR (s = "ELSE") OR (s = "ELSIF");
| 'F': res := (s = "FALSE") OR (s = "FOR");
| 'I': res := (s = "IMPORT") OR (s = "IN") OR (s = "IS") OR (s = "IF");
| 'M': res := (s = "MOD") OR (s = "MODULE");
| 'N': res := s = "NIL";
| 'O': res := (s = "OF") OR (s = "OR");
| 'P': res := (s = "POINTER") OR (s = "PROCEDURE");
| 'R': res := (s = "RECORD") OR (s = "REPEAT") OR (s = "RETURN");
| 'T': res := (s = "TO") OR (s = "THEN") OR (s = "TRUE") OR (s = "TYPE");
| 'U': res := s = "UNTIL";
| 'V': res := s = "VAR";
| 'W': res := s = "WHILE";
ELSE res := FALSE
END;
RETURN res
END IsReservedOberonWord;
Ну вот, простейшая хеш-таблица вышла )))
-
Ну вот, простейшая хеш-таблица вышла )))
Так и есть :-)
---------
К слову сказать, этот пример иллюстрирует, что оператор CASE это всё-таки нечто большее, чем просто синтаксический сахар. В случае использования равноценного многоветочного IF компилятору пришлось бы проанализировать условия во всех ветках для того чтобы понять сможет ли он сгенерировать эффективную таблицу переходов, или придётся генерировать код как для обычного IF'а.
-
К слову сказать, этот пример иллюстрирует, что оператор CASE это всё-таки нечто большее, чем просто синтаксический сахар. В случае использования равноценного многоветочного IF компилятору пришлось бы проанализировать условия во всех ветках для того чтобы понять сможет ли он сгенерировать эффективную таблицу переходов, или придётся генерировать код как для обычного IF'а.
Так и с CASE'ом тоже самое -- придётся анализировать, в каких диапазонах находятся эти метки и диапазоны меток, сортировать их, решать, можно ли сделать всё одной таблицей переходов или же придётся делать несколько таблиц переходов, или вообще проще IF-ами всё сравнивать...
Особой разницы для автора компилятора нет...
-
Так и с CASE'ом тоже самое -- придётся анализировать, в каких диапазонах находятся эти метки и диапазоны меток, сортировать их, решать, можно ли сделать всё одной таблицей переходов или же придётся делать несколько таблиц переходов, или вообще проще IF-ами всё сравнивать...
Особой разницы для автора компилятора нет...
Да, пожалуй. Значения меток выбора в общем случае могут быть сильно разряжены и располагаться неравномерно. С построением таблицы переходов могут возникнуть сложности:
VAR a: LONGINT;
CASE a OF
| -100500: ... ;
| 0, 1: ... ;
| 100500: ... ;
END
-
Так и с CASE'ом тоже самое -- придётся анализировать, в каких диапазонах находятся эти метки и диапазоны меток, сортировать их, решать, можно ли сделать всё одной таблицей переходов или же придётся делать несколько таблиц переходов, или вообще проще IF-ами всё сравнивать...
Особой разницы для автора компилятора нет...
Да, пожалуй. Значения меток выбора в общем случае могут быть сильно разряжены и располагаться неравномерно. С построением таблицы переходов могут возникнуть сложности:
VAR a: LONGINT;
CASE a OF
| -100500: ... ;
| 0, 1: ... ;
| 100500: ... ;
END
Не сложности, а даже невозможности :-) Идеальную хэш-функцию не построить в общем случае.
И как тут, в сосденей ветке, надавно заметили, репорт Оберона не запрещает меткам пересекаться. Что добавляет веселья компиляторщику :-)
-
Не сложности, а даже невозможности :-) Идеальную хэш-функцию не построить в общем случае.
К счастью, в нашем случае с ключевыми словами таких проблем не возникает. Хотя, надо сказать, что нет гарантии того, что какой-либо автор компилятора у себя в коде всё сделал правильно. :-)
-
Да, пожалуй. Значения меток выбора в общем случае могут быть сильно разряжены и располагаться неравномерно. С построением таблицы переходов могут возникнуть сложности:
VAR a: LONGINT;
CASE a OF
| -100500: ... ;
| 0, 1: ... ;
| 100500: ... ;
END
Не сложности, а даже невозможности :-) Идеальную хэш-функцию не построить в общем случае.
И как тут, в сосденей ветке, надавно заметили, репорт Оберона не запрещает меткам пересекаться. Что добавляет веселья компиляторщику :-)[/quote]
А учитывая, что в качестве меток могут быть не только константы, но и переменные, а также строковые литералы, совсем весело становится с таблицей переходов )))
-
А учитывая, что в качестве меток могут быть не только константы, но и переменные, а также строковые литералы, совсем весело становится с таблицей переходов )))
Ну, это не совсем таки так:
The case expression must be of type INTEGER or CHAR, and all labels must be integers or single-character strings, respectively.
А вот про константность того что в label'e действительно не сказано, хотя явно же ident там оставлен для именованных констант а не для переменных.
-
Наверняка такая мысль высказывалась уже. Но думаю цикл дейкстры в первую очередь удобен там, где после обычного цикла надо обработать еще что-то (прямо связанное с данным циклом). Правда в этом случае удобнее было бы сделать не ELSIF DO а просто ELSE и дальше уже условия цикла не проверять.
-
Не для этого нужен ЦД. Это цикл, содержащий внутри себя последовательность циклов.
-
- Почему в популярны языках нет Цикла Дейкстры?
- Потому что он там нинужен
:)
Если бы Влад изначально не заморочил мозги циклом Дейкстры, то выяснилось бы, что он и в этом примере нахрен не нужен.
static bool isReservedWorld2(string s, string words) {
int i = 0, w = 0;
bool prevEq = true;
while ((w < words.Length) && (i < s.Length)) {
if (prevEq && (words[w] == s[i])) {
i++;
} else if (words[w] == ' ') {
if (prevEq) {
i = 0;
} else {
prevEq = true;
}
} else if (prevEq) {
prevEq = false;
i = 0;
}
w++;
}
return i == s.Length && (w == words.Length || words[w] == ' ');
}
-
С учетом всех тем, где упоминался цикл Дейкстры, что я наблюдал, можно прийти к выводу, что в мире современных программистов ЦД - могучий артефакт, притягивающий к себе внимание вне зависимости от его целесообразности в конкретном случае. Знай бы об этом Никлаус Вирт, вероятно, захотел бы и его исключить из языка. Но с точки зрения маркетинга такая скандалообразующая штука безусловно полезна.
-
С учетом всех тем, где упоминался цикл Дейкстры, что я наблюдал, можно прийти к выводу, что в мире современных программистов ЦД - могучий артефакт, притягивающий к себе внимание вне зависимости от его целесообразности в конкретном случае. Знай бы об этом Никлаус Вирт, вероятно, захотел бы и его исключить из языка. Но с точки зрения маркетинга такая скандалообразующая штука безусловно полезна.
Самое смешное -- это то, что в Обероне нет цикла Дейкстры. Есть цикл, синтаксически напоминающий цикл Дейкстры, но семантически всё-таки отличающийся от него...
Впрочем, настоящего ветвления Дейкстры в Оберонах/Модулах-2 тоже нет, есть ветвление, лишь синтаксически напоминающее его.
-
С учетом всех тем, где упоминался цикл Дейкстры, что я наблюдал, можно прийти к выводу, что в мире современных программистов ЦД - могучий артефакт, притягивающий к себе внимание вне зависимости от его целесообразности в конкретном случае. Знай бы об этом Никлаус Вирт, вероятно, захотел бы и его исключить из языка. Но с точки зрения маркетинга такая скандалообразующая штука безусловно полезна.
Самое смешное -- это то, что в Обероне нет цикла Дейкстры. Есть цикл, синтаксически напоминающий цикл Дейкстры, но семантически всё-таки отличающийся от него...
Впрочем, настоящего ветвления Дейкстры в Оберонах/Модулах-2 тоже нет, есть ветвление, лишь синтаксически напоминающее его.
Предлагаю раскрыть мысль, дабы народ сейчас не нафантазировал чего-то странного.
-
Не для этого нужен ЦД. Это цикл, содержащий внутри себя последовательность циклов.
Да, но мозги при этом сломаешь больше, чем с обычными циклами. Да и условий дополнительных писать
-
С учетом всех тем, где упоминался цикл Дейкстры, что я наблюдал, можно прийти к выводу, что в мире современных программистов ЦД - могучий артефакт, притягивающий к себе внимание вне зависимости от его целесообразности в конкретном случае.
Да не. Просто после той мощной рекламной компании, которую устроил info21 для Цикла Дейкстры, хотелось найти хоть одно осмысленное применение на практике. А тут как раз язык с непосредственной поддержкой этого чуда. Вот и теперь, даже Петр Алмазов сказал, что Цикл Дейкстры тут не нужен. Будем искать :)
-
Нет и мне успокоения :)
Чуть подправлю свой последний вариант.
1. Без предположения, что слова в words отсортированы.
После последнего слова пробел обязателен.
PROCEDURE isReservedWorld(s: JsString.Type; words: JsString.Type): BOOLEAN;
VAR
i, w, с: INTEGER;
BEGIN
i := 0 ;
w := 0 ;
с := 0;
WHILE (w < JsString.len(words))
& ((с < JsString.len(s)) OR (i # JsString.len(s)) OR (JsString.at(words, w) # " ")) DO
IF (i < JsString.len(s)) & (JsString.at(words, w) = JsString.at(s, i)) THEN
INC(i);
INC(c);
ELSIF (JsString.at(words, w) # " ") THEN
INC(i);
ELSE
i := 0;
c := 0;
END;
INC(w);
END;
RETURN (w < JsString.len(words))
END isReservedWorld;
2. Если строка слов отсортирована, то
условие WHILE сокращается до
WHILE (w < JsString.len(words))
& (с < JsString.len(s)) DO
В этом случае будет выходить по первому совпадению проверяемого слова s с началом некоторого слова из words.
Возврат из функции тогда усиливается добавкой
RETURN ((w < JsString.len(words)) & (JsString.at(words, w) = " "))
-
Самое смешное -- это то, что в Обероне нет цикла Дейкстры. Есть цикл, синтаксически напоминающий цикл Дейкстры, но семантически всё-таки отличающийся от него...
Впрочем, настоящего ветвления Дейкстры в Оберонах/Модулах-2 тоже нет, есть ветвление, лишь синтаксически напоминающее его.
Предлагаю раскрыть мысль, дабы народ сейчас не нафантазировал чего-то странного.
Ну сколько ж можно раскрывать эту простую мысль? Я об этом много раз повторял (больше трёх точно).
Вспоминаем, что Дейкстра писал в своей "Дисциплине программироания" про оператор if (и аналогичные рассуждения по поводу оператора do):
if B1 → SL1 □ B2 → SL2 □ ... □ Bn → SLn fi
do B1 → SL1 □ B2 → SL2 □ ... □ Bn → SLn do
где B1...Bn -- "предохранители", логические выражения охраняемых команд,
SL1...SLn -- списки операторов,
а B1 → SL1 -- охраняемая команда
1. Предполагается, что все предохранители определены; если это не так, т.е. вычисление какого-то предохранителя может привести к работе без правильного завершения, то допускается, что и вся конструкция не сможет правильно завершить свою работу.
2. Вообще говоря, наша конструкция приведёт к недетерменированности при тех начальных состояниях, для которых истинны значения более чем одного предохранителя, поскольку остаётся неопределённым, какой из соответствующих списков операторов будет тогда выбираться для запуска. Никакой недетерменированности не возникает, если все предохранители попарно исключают друг друга.
3. Если начальное состояние таково, что ни один из предохранителей не является истиной, то мы встречаемся с начальным состоянием, для которого не подходит ни один из вариантов, а следовательно, и вся конструкция в целом. Запуск при таком начальном состоянии приведёт к отказу.
Замечание. Если мы допускаем также и пустой набор охраняемых команд, то операторы "if-fi" и "do-od" семантически эквивалентны нашему прежнему оператору "Отказать".
Таким образом, для того, что бы операторы IF и WHILE в Обероне могли считаться операторами if и do Дейкстры (назовём их "Ветвлением Дейкстры" и "Циклом Дейкстры"), их предохранители быть написаны в предположении недетерменированного порядка вычисления, и если ни один из предохранителей не срабатывает, должен происходить авост (HALT).
В рапорте об Обероне же прямо сказано:
The Boolean expression preceding a statement is called its guard. The guards are evaluated in sequence of occurrence, until one evaluates to TRUE, whereafter its associated statement sequence is executed. If no guard is satisfied, the statement sequence following the symbol ELSE is executed, if there is one.
...
While statements specify repetition. If any of the Boolean expressions (guards) yields TRUE, the corresponding statement sequence is executed. The expression evaluation and the statement execution are repeated until none of the Boolean expressions yields TRUE.
То есть семантика этих операторов отличается.
ЗЫ. Кстати, сам Дейкстра отнюдь не гнушался делать вложенные циклы и циклы с условиями внутри, а вовсе не всегда заморачивался с циклом своего имени. Например:
x, y, z := X, Y, 1;
do y ≠ 0 → do 2 | y → x, y := x*x, y/2 od;
y, z := y − 1, z*x
od {R стало истинным}
илиx, y, z := X, Y, 1;
do y<>0 → if non 2|y → y, z := y − 1, z*x
□ 2|y → пропустить fi;
x, y := x*x, y/2
od
хотя, казалось бы, уж здесь-то самое ему (ЦД) место...
-
Да не. Просто после той мощной рекламной компании, которую устроил info21 для Цикла Дейкстры, хотелось найти хоть одно осмысленное применение на практике. А тут как раз язык с непосредственной поддержкой этого чуда. Вот и теперь, даже Петр Алмазов сказал, что Цикл Дейкстры тут не нужен. Будем искать :)
Гм, я вроде бы и не ищу применимости ЦД, а тем не менее время от времени натыкаюсь на такие места, где он подходит, но никогда в работе его не использую, поскольку эмулировать его явно либо через макросы мне нравится. А в Обероне бы пошло.
Например, есть сканер, в котором работа с потоком данных ведётся с явными блоками памяти вместо абстракции посимвольного чтения над буферизированными данными. Это приводит к тому, что лексема может быть разделена между блоками. У меня чтение лексемы выглядит приблизительно так:
WHILE Suitable(buf[i]) DO
INC(i)
ELSIF buf[i] = NewPage DO
NextPage(buf, i, input)
END
-
Пару слов в "защиту" прав ЦД на жизнь :)
Мне кажется, что все сомнения в нужности ЦД, возникают из-за неучета нюансов просто циклов и циклов поиска.
Просто цикл - это когда нет бинарной интерпретации результата работы цикла, типа успешно/неуспешно и т.п.
В этом случае ЦД как обобщённый вариант вообще не должен вызывать никаких сомнений.
WHILE ~X DO ... END условие окончания X=TRUE ( или просто X)
соответственно для ЦД
WHILE ~X1 DO ...
ELSIF ~X2 DO ...
...
ELSIF ~Xn DO ...
END
условие окончания X1&X2&....Xn
В цикле поиска, грубо говоря, X=TRUE может и не наступить, поэтому условие WHILE должно это учитывать.
WHILE ~E & ~X DO ... END
условие окончания E OR X. Заданные условия E и X желательно должны быть таковы, что после окончания цикла выполнялось X=~E
Так вот в данном случае проблема для ЦД в том что условие выхода X1&X2&...Xn должно быть наглядно представимо в форме E OR X
-
Например, есть сканер, в котором работа с потоком данных ведётся с явными блоками памяти вместо абстракции посимвольного чтения над буферизированными данными.
1. Во многих случаях ЦД - это смешение разных уровней абстракций.
-
Так плохо это или хорошо?
В приведенном примере я отказался от лишнего слоя абстракции чтобы избавиться от ненужного кода и накладных расходов, связанных с ним. ЦД позволил сделать это просто. Теперь не нужно абстрагироваться над буфером, чтобы потом складывать прочитанное в другой буфер, потому что иначе неудобно.
-
Как говорится, it depends.
Но лозунг "Смело смешивайте разные уровни абстракции!" я бы не поддержал :)
-
Например, есть сканер, в котором работа с потоком данных ведётся с явными блоками памяти вместо абстракции посимвольного чтения над буферизированными данными.
Можно было бы отнести ЦД к разряду оптимизационных вещей (со всеми оговорками), если бы он не страдал (как правило) от пессимизации в виде многократного дублирования повторяющихся условий.
-
По сравнению с LOOP EXIT END цикл Дейкстры не несёт никаких оптимизаций, а только отсекает излишнию гибкость неструктурного цикла. Конечно, явная поддержка конструкции в языке даст оптимизирующему компилятору больше возможностей для ускорения, но такой компилятор выкинет и повторяющиеся проверки. Только это не в стиле Оберона, тем более что по моему опыту основной удар принимает первая ветка и повторные проверки не оказывают существенного влияния на время исполнения.