Оберон-клуб «ВЄДАsoft» https://zx.oberon.org/forum/ |
|
Цикл FOR. Следовать ли стандарту? https://zx.oberon.org/forum/viewtopic.php?f=46&t=83 |
Страница 1 из 2 |
Автор: | Saferoll [ 28 фев 2013, 17:07 ] |
Заголовок сообщения: | Цикл FOR. Следовать ли стандарту? |
Попробовал пример, с разными границами цикла. Получил зацикливание при Код: "OBERON"
Это дефект не ZXDev и не беззнаковых данных. Это общая проблема цикла FOR в Оберонах, как уже обсуждалось на форуме oberoncore. Но в данной системе проблема усугубляется еще и путаницей со знаковыми и беззнаковыми числами. В Турбопаскале и СпектрумБейсике такой проблемы c FOR не было. Предлагаю в ZXDev отступить от стандартной обероновской реализации FOR через WHILE и реализовать так, чтобы FOR x := A TO B обработало все значения в указанных границах, даже если B - это максимальное значение для данного типа. |
Автор: | Zorko [ 28 фев 2013, 19:28 ] |
Заголовок сообщения: | Re: Как работать с беззнаковыми числами в ZXDev/Ofront |
Saferoll писал(а): Предлагаю в ZXDev отступить от стандартной обероновской реализации FOR через WHILE и реализовать так, чтобы FOR x := A TO B обработало все значения в указанных границах, даже если B - это максимальное значение для данного типа. Но тогда мы потеряем совместимость со стандартом Оберона-2. Хотя велика ли потеря? Надо подумать. Я уже писал пару раз в рассылки, удивлялся насчёт этого момента. Отвечали что-то вроде “Если отступить от стандарта, то это будет уже не совсем Оберон”, в таком духе.Да, FOR v := n TO m интерпретируется согласно стандарту Оберона-2 как WHILE n <= m, т.е. цикл с конечным значением FOR byte := P.Unsigned(0) TO P.Unsigned(255) никогда не закончится, потому что byte ВСЕГДА будет <= 255. Конечно это несколько отличается от реализации FOR в других языках, и мне самому вот этот момент не очень нравится, но что поделать. Для цикла от 0 до 255 придётся использовать WHILE/DO, REPEAT/UNTIL или LOOP. Код: "OBERON"
ОК, я не возражаю против отступления от стандарта, но нам надо придумать во что превратить сишный while(n <= m), в который транслируется обероновский FOR. |
Автор: | Zorko [ 01 мар 2013, 08:50 ] |
Заголовок сообщения: | Re: Как работать с беззнаковыми числами в ZXDev/Ofront |
Посмотрел как сделан цикл FOR в TurboPascal и Delphi: Код: "EMPTY" ; Turbo Pascal Код: "EMPTY" ; Delphi Как видим, TP делает две проверки в цикле, Delphi же действует более изощрённо: ещё до вхождения вычисляет количество повторений цикла и хранит в отдельной внутренней переменной. После некоторых раздумий чтобы не завязываться на переполнении предлагаю такие варианты. Было: Код: "EMPTY" Unsigned__for__1 = Platform_Unsigned(255); (* Конечное значение цикла *) Вариант 1: Код: "EMPTY" Unsigned__for__1 = Platform_Unsigned(255); (* Конечное значение цикла *) Кстати, эта задачка пересекается с ещё не реализованным поведением MIN(UTYPE)/MAX(UTYPE). Вариант 2: Код: "EMPTY" Unsigned__for__1 = Platform_Unsigned(255); (* Количество повторений цикла - 1 *) Ещё такое поведение FOR надо обязательно сделать опциональным (и выключенным по умолчанию), опять же из соображений совместимости со стандартом. Saferoll, если не хотите сами заняться этой доработкой, я добавлю себе в todo, но не обещаю сделать быстро. Здесь ещё есть над чем подумать. |
Автор: | Zorko [ 01 мар 2013, 10:46 ] |
Заголовок сообщения: | Re: Как работать с беззнаковыми числами в ZXDev/Ofront |
Прочитал ветку на Оберонкоре. Да, этот вариант вроде бы ничего: FOR v := 0 TO P.Unsigned(255) BY 1 DO statements END; Код: "OBERON"
В ходе исполнения кода v := v - step; REPEAT v := v + step может возникнуть заём 0-1 и переполнение 255+1 (65535+1), что вобщем-то не должно вызвать проблем при работе Си-программы. |
Автор: | Saferoll [ 02 мар 2013, 11:24 ] |
Заголовок сообщения: | Re: Как работать с беззнаковыми числами в ZXDev/Ofront |
Zorko писал(а): ОК, я не возражаю против отступления от стандарта... Жаль... ![]() ![]() Ничего-о, я все равно стандарт раскритикую, другие тоже достойны просветления. ![]() Ну вот хотя бы к этому придерусь: Цитата: Ещё такое поведение FOR надо обязательно сделать опциональным (и выключенным по умолчанию), опять же из соображений совместимости со стандартом. Думаю, что надо включить по умолчанию. Хотя бы включить. А лучше вообще без возможности отключать. А еще лучше не только в этой системе, но и во всех Оберонах. Ибо плохой это стандарт, неверный, неразумный, ошибочный. А уж в ZXDev и вовсе не к месту. С каким самым первым ЯП сталкивается пользователь Спекки? ...? Да-да, наш любимый примитивный, неструктурный, немодульный, но именно его зашил в эту волшебную коробочку сэр Клайв (даже еще и не будучи сэром). И какой в этом языке штатный оператор цикла (и единственный,кстати)? ...? Угу-именно он! И если мы будем портировать игру на ЛазерБейсик, то в какой оператор Оберона он отобразится? Риторически! Запустим мы этот порт и получим.... Хотя все вроде верно. Так ведь это стандарт Оберона - зацикливание получать! ![]() Мы пока что изменяем FOR только в трансляции через Ofront, а модули составляющие сам ББ скомпилированы по стандарту. И это нормально, что есть особенности для наших специфических целей. Зато проблем с таким FOR гораздо меньше. Разве лучше, опцию по умолчанию выключить, чтобы все ее всегда включали вручную? По умолчанию должен быть самый употребительный вариант, а здесь он - "незацикливающийся FOR". Вот экран у нас 256 точек, поставили в цикле обработку - на правом краю экран завис. Завели буфер для фрагментов в 256 байт. Если выбираем фрагмент примыкающий к концу буфера ( TO 255) - зависание! Для вас не звучит странно "По стандарту оператор FOR в Обероне иногда должен зависать, поэтому это опция по умолчанию. Но если вы хотите, чтобы FOR мог выполниться для любых значений параметров, то переключите опцию"? |
Автор: | Saferoll [ 02 мар 2013, 11:53 ] |
Заголовок сообщения: | Re: Цикл FOR. Следовать ли стандарту? |
Zorko писал(а): Но тогда мы потеряем совместимость со стандартом Оберона-2. ... Я уже писал пару раз в рассылки, удивлялся насчёт этого момента. Отвечали что-то вроде “Если отступить от стандарта, то это будет уже не совсем Оберон”, в таком духе. ИМХО, если следовать такому стандарту, то вот тогда будет не совсем Оберон. Раз уж стандарт задает семантику FOR через эквивалентный фрагмент кода на Обероне, то, тем самым, стандарт задает и все особенности исполнения этого кода при разном сочетании параметров. Т.е. стандарт определяет семантику FOR так (возьмем для простоты шаг 1): "FOR x:=A TO B DO ...END выполнит тело цикла для всех значений x из отрезка A..B в возрастающем порядке. Затем, если B=MAX(T), то тело цикла будет выполняться бесконечное число раз, перебирая циклически все значения типа T." Ну, или вариант (если включен контроль за переполнением чисел): "..., то исполнение программы аварийно прервется из-за переполнения". Помилуйте, это стандарт?! ![]() Контроль за переполнением типов находится за рамками самого ЯП? Ладно, пусть так. Но ограниченность множества значений целочисленного типа язык осознает? См п.6.1. Язык программирования Оберон-2 Цитата: 3. SHORTINT целые в интервале от MIN(SHORTINT) до MAX(SHORTINT) 4. INTEGER целые в интервале от MIN(INTEGER) до MAX(INTEGER) 5. LONGINT целые в интервале от MIN(LONGINT) до MAX(LONGINT) На разных платформах эти границы разные, но они везде есть, потому что заданы стандартом! В операторе FOR x:=A TO B x,A,B могут принимать любые значения из типа, которому принадлежат, в т.ч. и максимальное. И если B=MAX(T), то последняя итерация выполнится для x=MAX(T). А потом стандарт требует инкремент — х должно принять значение на 1 больше чем максимально возможное. И вот тут будет или бесконтрольное переполнение, или аварийный выход или...Что еще можно придумать? Ну например, инкремент не изменит в этом случае значение x (такая вот реализация — барьер там стоИт). Но во всех этих трех реализациях будет или зацикливание или АВОСТ. Т.е. С одной стороны Оберон говорит, что для переменной есть максимальное значение, с другой стороны приказывает и в этом случае выполнять инкремент. Что при этом случится зависит от реализации, это вне ЯП. Но уж понимать, что при любой реализации ничего хорошего от превышения не получится, это-то в рамках языка? Код: "OBERON"
Задача четко сформулирована, вроде бы нигде выхода за границу не видно (в том числе и при В=МАХ(Т)). А получаем в некоторых случаях либо зацикливание, либо ТРАП! Почему? А потому, что после обработки последнего элемента x=B задача уже выполнена, тут бы и прекратить работу — это была бы самая разумная, естественная семантика цикла. Нет же, по стандарту надо после этого выполнить инкремент. Зачем? Зачем брать следующий элемент, который мы не должны обрабатывать (и который в данном частном случае вообще не существует!)? Тебе разрешили съесть 10 конфет, а ты 10-ю съел и за 11-й потянулся? Не съел, но ведь взял же, взял! Так ведь дадут по рукам-то! И правильно сделают, давно по рукам врезать пора! |
Автор: | Saferoll [ 02 мар 2013, 14:35 ] |
Заголовок сообщения: | Re: Цикл FOR. Следовать ли стандарту? |
Сказка о Великом Стандарте Представим, что задали ученику такую задачу: Код: "EMPTY" Связное подмножество целых чисел T задано своими границами MIN(T)..MAX(T). Связная подобласть этого подмножества задана границами А..B (MIN(T)<=A,B<=MAX(T)). Часто встречающаяся задача, правда? В строке обработать подстроку, в массиве подмассив и т. д. Вот написал наш ученик программу (через WHILE), стали ее запускать. Всё хорошо - пустую подобласть обрабатывает, область в левом краю Т тоже, в середине тоже... А вот когда обрабатываем правый край Т (когда B=MAX(T)) – зависает! Что это, ошибка? Ну так-то да, типичная ошибка при обработке линейной структуры - «out of range”/”плюс-минус 1” - не учтена специфика граничных элементов (в данном случае последнего, а иногда первый тоже надо как-то особо обработать). Можно поставить «незачет» и заставить исправить. Но если ученик — любимое чадо высокопоставленного отца, то... То может лучше глаза закрыть на такую недоработку? Ведь частично же работает, не всегда же подобласть справа будет или всё T займет. Массивы с нуля нумеруются, память ограничена, диапазон Т велик — так может массив туда и не дотянется, может и не понадобится нам MAX(T) обрабатывать? А придется...так что же, скажем «фича это такая, особенность», не дОлжно на всю область рот розевать, надо хоть 1 элементик в конце нетронутым оставить. Если применяете для обработки всего Т (или подобласть на правом краю), то сами и виноваты, что не умеете под эту ошиб...э-э-э особенность подстраиваться! А чтоб уж все подстроились, программу эту для обработки подобласти в стандарт возведем, чтоб и у всех так же было. А то что ж, одни элементик последний оставляют, а другие всё обрабатывать горазды? Пусть теперь у всех задача об обработке подобласти на краю зависает! Ну а теперь задачу эту назовем «реализация оператора FOR”, ученика Обероном наречем, его родитель(ли) … - сами-знаете-кто ![]() «Да здравствует созданный волей народной Великий Могучий Стандарт Оберо-о-о-н! Сла-а-а-вься-а-а...» |
Автор: | Zorko [ 02 мар 2013, 19:20 ] |
Заголовок сообщения: | Re: Как работать с беззнаковыми числами в ZXDev/Ofront |
Saferoll писал(а): Zorko писал(а): ОК, я не возражаю против отступления от стандарта... Жаль... ![]() ![]() Но если требуются аргументы против, то пожалуйста. ![]() Я полагаю, что актуальность данного вопроса в контексте 32-битных платформ незначительна, и вот аргументация. Если в качестве счётчика цикла использовать 32-битные и больше целые, то вероятность достижения TO MAX(INTEGER), а уж тем более TO MAX(LONGINT) невероятно мала, стоит очень напрячь мозг, чтобы получить задачу, где такое значение действительно потребуется. В то же время использовать в качестве счётчика цикла переменную типа SHORTINT или BYTE особого смысла нет, т.к. это не даёт никаких преимуществ на 32-битных платформах. Собственно, Ofront даже не даст использовать переменную типа SYSTEM.BYTE как счётчик цикла (и наверное это правильно). Так что, как видим, проблемы нет. Достаточно просто знать, что цикл FOR реализован без учёта конечного значения переменной, и использовать это. Вернее, редко с этим сталкиваться, и если очень уж повезёт. Однако ситуация крайне меняется если нужно программировать 8- и 16-битные процессоры. Здесь уже без маленьких типов данных обойтись не получится (из соображений эффективности), и соответственно нужно с ними как-то работать. Здесь проблема имеет место. Именно в таком проекте как XDev. Но 99% оберонщиков назовут это несущественным тоже, ибо им уже не нужны такие процессоры. Получается, желание ворошить стандарт исходит из практической необходимости разрабатывать под старинные процы, как я понимаю. Ну и что я должен был ответить? Отказаться реализовывать? ![]() Saferoll писал(а): Для вас не звучит странно "По стандарту оператор FOR в Обероне иногда должен зависать, поэтому это опция по умолчанию. Но если вы хотите, чтобы FOR мог выполниться для любых значений параметров, то переключите опцию"? Звучит-звучит. ![]() ![]() Увы, со стандартами приходится считаться, хотя они и не всегда хороши. И, кстати, не могу не пойти навстречу юзеру ZXDev, ибо нас так мало. ![]() Ну а Вирту своейственно избегать проблем и оставлять их за спиной, без внимания. Видишь, ввели пару сущностей, чтобы работать с беззнаковыми — уже сколько появилось тонких моментов. Тут, опять же, FOR — просто массу проблем порождает. Например, можешь сходу ответить: FOR i := 1 TO Fn() DO — выполнит Fn() один раз? Или столько, сколько раз исполнится цикл? Хорошо, что мы это знаем, но если не знать семантики языка, то из самой записи это не следует. |
Автор: | Saferoll [ 02 мар 2013, 23:31 ] |
Заголовок сообщения: | Re: Как работать с беззнаковыми числами в ZXDev/Ofront |
Zorko писал(а): Я полагаю, что актуальность данного вопроса в контексте 32-битных платформ незначительна, и вот аргументация. Если в качестве счётчика цикла использовать 32-битные и больше целые, то вероятность достижения TO MAX(INTEGER), а уж тем более TO MAX(LONGINT) невероятно мала, стоит очень напрячь мозг, чтобы получить задачу, где такое значение действительно потребуется. Не забывайте, что проблема имеет место, когда B=MAX(LONGINT) или MAX(INTEGER), граница А в этом случае может быть любой (потому что всегда А<=B=MAX(LONGINT) ), т.е. сама подобласть может состоять всего из нескольких чисел (например MAX(INTEGER)-10 TO MAX(INTEGER)), но сами числа очень большие - максимальные. Конечно, это вряд ли индексы массива. Но почему не параметры в каком-то целочисленном вычислении: датчик СЧ, криптография, поиск целых с заданными свойствами... Например, 2^32-1 - это двоичный репьюнит,он особыми свойствами обладает, может быть потребуется сделать какие-то вычисления вблизи этого числа? Вот получила ракета закодированную команду, взяла очередной случайный ключ (а он как назло сегодня MAX(LONGINT)!), выполнила в алгоритме расшифровки FOR вблизи этого числа и ... Жалко, миллиарды долларов стоила. Ну не протестировали, потому что по матпостановке ключ К в алгоритме для любого числа LONGINT справедлив. Поэтому особо не проверяли, понадеялись что достаточно VAR K:LONGINT; Цитата: Однако ситуация крайне меняется если нужно программировать 8- и 16-битные процессоры. Здесь уже без маленьких типов данных обойтись не получится (из соображений эффективности), и соответственно нужно с ними как-то работать. Да, для Z80 и прочих "малышей" это особенно актуально. Хотя, повторю, свалится в эту "каверну" на любой системе можно.Цитата: Так что, как видим, проблемы нет. Да есть проблема-то, и как я понял, агромадная! Она даже фундаментальней чем цикл FOR, это проблема методическая. Цитата: Достаточно просто знать, что цикл FOR реализован без учёта конечного значения переменной, и использовать это. А многие обероновцы знают? А многие из мэтров (хотя бы с oberoncore) осознают такие особенности своих программ, принципиально не употребляя FOR, но воспроизводя через WHILE эквивалентную "стандартную" конструкцию (и,конечно, с таким же дефектом)? И многие пишут в документации к модулям кроме VAR N:INTEGER еще и N<MAX(INTEGER) или ставят это условие в ASSERT? Это и есть методическая проблема.Цитата: Вернее, редко с этим сталкиваться, и если очень уж повезёт. Читайте выше про ракету, ей сегодня "повезло". А могло бы "повезти" через год. Это хорошо, когда такая бомба замедленного действия есть? Ведь Оберон позиционируется как очень надежный язык, с доказательствами правильности, с выводом свойств, с пред- и постусловиями. А когда легче все это выводить: когда утверждения справедливы для всего множества значений или когда в области значений есть "лакуна", где особые (и "некрасивые") свойства? Если в программе написано VAR N:INTEGER;, то уж вряд ли в N будет что-то другое, за этим компилятор внимательно следит. И если какое-то свойство справедливо для всех INTEGER, то и проверять ничего особо не нужно, всё будет само собой. А вот если дополнительно нужно N<MAX(INTEGER), то нужны спецповерки, ASSERT или еще что-то. А если таких параметров несколько, как проявится их сочетание? Пред\пост-условия превращаются в многоэтажную конструкцию из дизъюнктов и конъюнктов, из которой ничего полезного уже не вывести. Ну не работает интуиция на таких запутанных вещах, ей что-то "красивое", регулярное, "разумное" нужно!Да и с какой стати мы должны отбрасывать значение MAX? Ну ради чего? Ради старой ошибки стандарта? Давайте лучше ошибку отбросим! Цитата: Подсчитанный нами десятитысячелетний срок означает, что за время своего существования умножитель должен будет выполнить только ничтожную часть огромного числа всех возможных перемножений, на которые он способен: практически ничего он сделать не успеет. Как ни странно, мы все же требуем, чтобы он был способен правильно выполнить любое перемножение, если поступит соответствующий приказ. Это фантастическое качество требуется потому, что мы не знаем заранее, какие именно ничтожно малые по своему количеству умножения потребуется выполнить.(Э.Дейкстра.Структурное программирование) А вот еще вспомнилась Ошибка Pentium FDIV. Intel тоже пыталась за фичу выдать, "потому что чисел много и вероятность получит ошибку крайне мала, менее вероятности быть убитым метеоритом (хотя если вы знаете куда упадет метеорит, вы можете встать на это место)"(точную цитату не помню и сразу в Инете не нашел, но смысл такой). И что? Скандал поднялся и "фичу" исправили, потому что это явный баг. Процессор должен без ошибок любые числа делить, а FOR в любых границах итерации выполнять!Цитата: Ну и что я должен был ответить? Отказаться реализовывать? Должен был согласиться. ![]() И, кстати, не могу не пойти навстречу юзеру ZXDev, ибо нас так мало. ![]() ![]() ![]() ![]() ![]() Цитата: Вот мне не нравится заём и переполнение счётчика цикла. В ассемблере и Си ещё куда ни шло, но Оберону приличествует обрабатывать такие ситуации (хотя бы в отладочном режиме, если не всё время). Переполнение в цикле происходит от неверной организации цикла - после обработки последнего элемента не нужно брать следующий (выполнять инкремент). А само переполнение...Мы работаем на 8-разрядном процессоре, переполнения не избежать. Управлять...ну вроде в самом Обероне управления нету, выходит за рамки языка. Разве что спецSYSTEMпроцедуры написать, чтоб ассемлерные макросы вставлять.Погоди-ка, что ты сказал?! "Си"?! Ну конечно же, СИ! Вот откуда всё это тянется-то... ![]() Ведь стандартный обероновский FOR это эквивалент сишного for(x=A;x<=B;x++); Перешли на структурный ЯП, и дефектный шаблон из Си протащили. Так-так, говорил же, методическая проблема, в головах, в мышлении, в инерции - вот где главное зло. Цитата: Но поправочка. "Хотите вкусного FOR'а — юзайте волшебную опцию и наслаждайтесь. Но если хотите, чтобы это ещё и не зависало на других Оберонах — тогда уж помилуйте. И не говорите, что мы вас не предупреждали." Поправочка к поправочке "...Но если хотите, чтобы это ещё и зависало на других Оберонах" Ведь стандарт-это зависание в некоторых случаях, волшебная опция зависание убирает. А где не зависало, там без разницы - полностью так же работает.![]() Цитата: Увы, со стандартами приходится считаться, хотя они и не всегда хороши. Ну зачем считаться с плохими стандартами, чтоб всем стандартно плохо было?Цитата: Тут, опять же, FOR — просто массу проблем порождает. Например, можешь сходу ответить: FOR i := 1 TO Fn() DO — выполнит Fn() один раз? Или столько, сколько раз исполнится цикл? Хорошо, что мы это знаем, но если не знать семантики языка, то из самой записи это не следует. Если не знать семантики, то из любой записи ничего не следует, потому что семантика - это и есть смысл. Я не против задания семантики фрагментом кода на Оберон. Я говорю, что в этом фрагменте для FOR ошибка, он должен быть другой, правильный. Чтобы все значения границ обрабатывал и не зацикливался. А какой именно, надеюсь скоро написать. |
Автор: | Влад Жаринов [ 03 мар 2013, 10:49 ] |
Заголовок сообщения: | О границах параметра цикла FOR |
По ходу, "лишний" инкремент может иметь смысл, если трактовать параметр как указатель позиции. Который должен иметь также положение "после конца" (и, кстати, "до начала" тогда уж ![]() |
Страница 1 из 2 | Часовой пояс: UTC + 2 часа |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |