Оберон-технология: особенности и перспективы |
Тематика обсуждения: Оберон-технология. Особенности, перспективы, практическое применение.
Всего в теме 6256 сообщений
Добавить свое сообщение
Отслеживать это обсуждение  Обсуждение из раздела Школа ОБЕРОНА
№ 5686 21-10-2007 06:44 |  |
Ответ на »сообщение 5685« (Стэн)
___________________________
Ответ на »сообщение 5684« (Илья Ермаков)
___________________________
Вот цикл, который прямо таки подкупает легкостью своего чтения и понимания... Но в нем есть ошибки - где они?
Ну и какое отношение имеет сей код (и не с WHILE, а с многими выходами) к обсуждаемому вопросу? Не выкинули сложную начинку во вложенные процедуры, но цикл тут причём?
Кстати, если разработчики Aos любят LOOP ... END, то вот Oberon Microsystems - нет (в исходниках Блэкбокса он встречается очень редко). Они в аналогичных ситуациях вводят в теле цикла специальную переменную - состояние (включающую в том числе ошибочные ситуации). И строят как сам цикл, так и развилки и вложенные циклы внутри него с использованием этой переменной. Получается гораздо лучше, чем с кучей выходов - код непрерывен и поддаётся внимательному построчному анализу.
А после окончания цикла по переменной сразу видно, по какому поводу цикл завершён. И эта же переменная может идти наверх как результат успеха-неуспеха. Можно даже конечный автомат нарисовать для построения такого цикла.
№ 5685 21-10-2007 06:29 |  |
Ответ на »сообщение 5684« (Илья Ермаков)
___________________________
>>> Вот Вам и разница в мотивах. Меня не очень волнует "кто сказал, что программист не ошибётся". Опытному программисту для таких несложных циклов, действительно, всё равно, как написать, и если он в уме прокрутил условия на цикл, то он запишет его верно.
>>> Меня волнует, как будет выглядеть чтение и понимание этого цикла.
Вот цикл, который прямо таки подкупает легкостью своего чтения и понимания... Но в нем есть ошибки - где они?
LOOP
REPEAT
p.Receive(buf, 0, LEN(buf), 0, fip, fport, len, res);
UNTIL res = AosUDP.Timeout;
exit := FALSE;
INC(xid);
AosOut.Enter; AosOut.String("AosDHCP: Discover - xid "); AosOut.Int(xid, 0); AosOut.Exit;
time := AosKernel.GetTimer();
msgLen := CreateDHCPDiscoverMsg(int.dev.local, xid, 0, buf);
p.SendBroadcast(int, BootPServer, buf, 0, msgLen);
p.Receive(buf, 0, LEN(buf), offerDelay, fip, fport, len, res);
IF (res = AosUDP.Ok) & (fport = BootPServer) & (len >= 28) & (buf[0] = 2X) & (xid = AosNet.Get4(buf, 4)) THEN
localAdr := AosNet.Get4(buf, 16);
AosOut.Enter; AosOut.String("AosDHCP: BootP reply from "); AosIP.OutAdr(AosNet.Get4(buf, 20));
AosOut.String("; IP offered: "); AosIP.OutAdr(localAdr); AosOut.Exit;
IF len > 236 THEN
ParseOptions(buf, 236, len, maskAdr, gatewayAdr, dns, domain, serverIP, rxid, msgType);
AosOut.Enter; AosOut.String("AosDHCP: Offer received - xid "); AosOut.Int(rxid, 0);
AosOut.String( " msgType "); AosOut.Int(ORD(msgType), 0); AosOut.Exit;
IF (rxid = xid) & (msgType = 2X) THEN
requestTries := 1; requestDelay := 2 * AosKernel.second;
LOOP
REPEAT
p.Receive(buf, 0, LEN(buf), 0, fip, fport, len, res);
UNTIL res = AosUDP.Timeout;
time := AosKernel.GetTimer();
AosOut.Enter; AosOut.String("AosDHCP: Request - xid "); AosOut.Int(xid,0); AosOut.Exit;
msgLen := CreateDHCPRequestMsg(int.dev.local, xid, 0, buf, serverIP, localAdr);
p.SendBroadcast(int, BootPServer, buf, 0, msgLen);
p.Receive(buf, 0, LEN(buf), requestDelay, fip, fport, len, res);
IF (res = AosUDP.Ok) & (fport = BootPServer) & (len >= 28) & (buf[0] = 2X) THEN
IF (localAdr = AosNet.Get4(buf, 16)) THEN
ParseOptions(buf, 236, len, maskAdr, gatewayAdr, dns, domain, serverIP, rxid, msgType);
AosOut.Enter; AosOut.String("AosDHCP: Ack - xid "); AosOut.Int(rxid,0);
AosOut.String( " msgType "); AosOut.Int(ORD(msgType), 0); AosOut.Ln;
AosOut.String(" localIP: "); AosIP.OutAdr(localAdr);
AosOut.String("; mask: "); AosIP.OutAdr(maskAdr);
AosOut.String("; gateway: "); AosIP.OutAdr(gatewayAdr);
AosOut.Exit;
exit := FALSE
ELSE
AosOut.Enter; AosOut.String("AosDHCP: Nack - xid "); AosOut.Int(rxid,0);
AosOut.String( " msgType "); AosOut.Int(ORD(msgType), 0); AosOut.Ln;
AosOut.Exit;
localAdr := AosIP.NilAdr;
exit := TRUE;
res := 3;
END;
EXIT
END;
sleep := offerDelay - (AosKernel.GetTimer() - time);
IF sleep > 0 THEN
t.Sleep(sleep);
END;
IF requestTries >= MaxRequestTries THEN
AosOut.Enter; AosOut.String("AosDHCP: Retransmission limit reached"); AosOut.Exit;
END;
INC(requestTries); requestDelay := requestDelay * 2
END;
IF exit THEN EXIT END
ELSE
res := 2;
END
ELSE
res := 1;
END
END;
sleep := offerDelay - (AosKernel.GetTimer() - time);
IF sleep > 0 THEN
t.Sleep(sleep);
END;
offerDelay := offerDelay*3 DIV 2;
INC(offerTries);
IF offerTries > MaxOfferTries THEN
res := AosUDP.Timeout;
EXIT;
END;
END;
№ 5684 21-10-2007 06:03 |  |
Ответ на »сообщение 5682« (Стэн)
___________________________
Ответ на »сообщение 5680« (Илья Ермаков)
___________________________
Тот факт, что lexem на выходе не равен LEX_DEF конечно важен, но кто сказал, что программист не ошибется в группе этих строчек?
Какая разница, что искать - место с BREAK или некорректную логику присваивание условной переменной?
Вот Вам и разница в мотивах. Меня не очень волнует "кто сказал, что программист не ошибётся". Опытному программисту для таких несложных циклов, действительно, всё равно, как написать, и если он в уме прокрутил условия на цикл, то он запишет его верно.
Меня волнует, как будет выглядеть чтение и понимание этого цикла.
№ 5683 21-10-2007 06:00 |  |
Ответ на »сообщение 5665« (Руслан Богатырев)
___________________________
>>>По-моему, в отношении FOR дело несколько проще. Для Вирта введение этого цикла в язык -- компромисс, на котором настаивают другие. Ему этот цикл похоже не нравится. В любом случае в классическом Паскале, классической Модуле-2 запрещалось изменение значения управляющей переменной внутри цикла. Явно.
Да, верно. (На всякий случай все же проверил с помощью компилятора XDS. :) )
Забавно: я читал у Вирта, что управляющая переменная "не должна" меняться в теле цикла, но ошибочно воспринимал это как "всего лишь" рекомендацию.
Но вопрос все же поставлен шире.
Возьмем пример, который несколько раз приводил Стэн: WHILE (i < len) & Condition(i) DO ... END; где Condition(VAR i: INTEGER): BOOLEAN (возможно) меняет значение переменной i.
То, что обнаружение (возможных) побочных эффектов для компилятора не представляет проблемы, было продемонстрировано с помощью цикла FOR.
Но все же такая конструкция не запрещена, так же как не запрещены функции с побочными эффектами.
Братья-функциональщики спрашивают: WHY-Y-Y? :)
Возникла гипотеза ( »сообщение 5654«), что в виртовских языках два уровня строгости: обязательный и "рекомендательный". Компилятор не запрещает некоторые отклонения от "хорошего стиля", которые он может обнаружить. (В качестве примера я сослался на компилятор и анализатор КП в ББ.)
Как Вы относитесь к такому предположению?
№ 5682 21-10-2007 05:59 |  |
Ответ на »сообщение 5680« (Илья Ермаков)
___________________________
>>> Я бы оставил свой вариант, но, поскольку один и тот же lexem нужен два раза, ввёл его отдельной переменной.
Отлично, только таким образом мы извратили всю идею условий и инвариантов в циклах. Тот факт, что lexem на выходе не равен LEX_DEF конечно важен, но кто сказал, что программист не ошибется в группе этих строчек?
lexem = result.lexem;
if ( lexem == LEX_DEF )
Какая разница, что искать - место с BREAK или некорректную логику присваивание условной переменной?
№ 5681 21-10-2007 05:52 |  |
Ответ на »сообщение 5678« (Илья Ермаков)
___________________________
>>> Ну, точно так же и будет использоваться на новом шаге итерации. Цикл будет работать точно так же. Только вместо ветки ELSE return в тот же секунд произойдёт выход из цикла по ложности условия. А после цикла - всё тот же return, который я не написал.
Нет, не также...
Выражение result = prs_def().parse( result.str, params ); перезапишет result.lexeme
Выражение while ( result.lexeme == LEX_DEF ); даст FALSE и выйдет из цикла в место того, чтобы продолжить выполнение...
Более того, структура result в общем случае у меня является вариантной записью, поэтому некоторые поля могут быть не определены. Поэтому некорректно ждать что prs_def().parse( result.str, params ) не возвратит result.lexeme == LEX_DEF, может и возвратить, поэтому Ваш вариант не исправляется простой заменой условий в выражении на result.lexeme != LEX_DEF.
№ 5680 21-10-2007 05:50 |  |
Я бы оставил свой вариант, но, поскольку один и тот же lexem нужен два раза, ввёл его отдельной переменной.
lexem = result.lexem;
if ( lexem == LEX_DEF )
result = ...;
while ( lexem == LEX_DEF );
В любом случае, я бы сначала построил каркас цикла, а уже потом смотрел, как там и что в нём меняется. И если бы изменение мешало проверке условия, просто запомнил бы значение в отдельной переменной.
№ 5679 21-10-2007 05:46 |  |
Ответ на »сообщение 5678« (Илья Ермаков)
___________________________
А, ну да, Вы модифицируете result, и вторая проверка в условии цикла уже не будет совпадать с первой.
Ща подумаем.
№ 5678 21-10-2007 05:43 |  |
Ответ на »сообщение 5676« (Стэн)
___________________________
Ответ на »сообщение 5674« (Илья Ермаков)
___________________________
Не имеет значение, что возвращает prs_def().parse( result.str, params ) в result.lexeme, важно, что она модифицирует result.str, которое используется на новом шаге итерации...
Ну, точно так же и будет использоваться на новом шаге итерации. Цикл будет работать точно так же. Только вместо ветки ELSE return в тот же секунд произойдёт выход из цикла по ложности условия. А после цикла - всё тот же return, который я не написал.
№ 5677 21-10-2007 05:41 |  |
Ответ на »сообщение 5673« (Стэн)
___________________________
Ответ на »сообщение 5671« (Илья Ермаков)
___________________________
А Вы знаете, что наши каратисты очень часто побеждают японцев на чемпионатах, хотя япония считается родиной каратэ (для некоторых стилей)? А знаете почему? Потому, что в японии четкое следование традиции и технике непреложная истина, а наши допускают "творческое отступление от канона", поэтому более эффективны...
Пример понят. Пример хороший.
Идея Вашего примера в чём? Есть некорые "традиции", "каноны" в искусстве (а в науке-технике - правила грамотной работы), которые хорошо работают в большинстве ситуаций. Но всегда есть "особые точки", в которых требуется импровизация. И именно этой импровизацией, способностью при необходимости отклониться от канонов и обусловлен уровень мастерства, и победа в схватке.
А что у нас с программированием? Да, "особые точки" тоже есть. Но, во-первых, для того, чтобы в них "импровизировать", требуется чёткое владение и применение канонов во всех обычных случаях. Этого не наблюдается, наоборот - повальная "импровизация" всюду, "пратизанщина с вилами", вместо использования обычных, нормальных средств. Начиная от ваяния самопальных циклов и кончая ваянием самопальных алгоритов-велосипедов, на порядок худших общеизвестных аналогов.
Во-вторых, к вопросу о циклах - ценой использования "канонов" и в малом множестве "особых точек" будет далеко не проигрыш на ринге, и даже не некоторая потеря эффективности а, может быть, пара лишних строк. Значение всего этого так исчезающе мало, что стоит ли заморачиваться даже мыслями об этих "импровизациях"? Не лучше ли подумать над задачей.
Добавить свое сообщение
Отслеживать это обсуждение 
Дополнительная навигация: |
|