LINUX.ORG.RU

[C] порядок выполнения операций.

 


0

0

Я что-то не так понимаю? Код

double ostexpr()
{
	double d;
	get_token();
	switch (curr_tok) {
	case PLUS:
		return term() + ostexpr();
	case MINUS:
		d = term();
		return -d + ostexpr();
	default:
		pushback ++;
		return 0;
	}
}
работает корректно, а код
double ostexpr()
{
	double d;
	get_token();
	switch (curr_tok) {
	case PLUS:
		return term() + ostexpr();
	case MINUS:
		return -term() + ostexpr();
	default:
		pushback ++;
		return 0;
	}
}
приводит к ошибке. В чем дело, в том, что ostexpr вызывыается раньше term?

★★

Ответ на: комментарий от wieker

[code]
В языке «C», как и в большинстве языков, не фиксируется
порядок вычисления операндов в операторе. Например в опера-
торе вида

X = F() + G();

сначала может быть вычислено F, а потом G, и наоборот; поэ-
тому, если либо F, либо G изменяют внешнюю переменную, от
которой зависит другой операнд, то значение X может зависеть
от порядка вычислений. Для обеспечения нужной последователь-
ности промежуточные результаты можно опять запоминать во
временных переменных.
Подобным же образом не фиксируется порядок вычисления
аргументов функции, так что оператор

PRINTF(«%D %D\N»,++N,POWER(2,N));



может давать (и действительно дает) на разных машинах разные
результаты в зависимости от того, увеличивается ли N до или
после обращения к функции POWER. Правильным решением, конеч-
но, является запись

++N;
PRINTF(«%D %D\N»,N,POWER(2,N));

[/code] (С) K&R

arhibot
()

К какой ошибке?

anonymous
()

> return -term() + ostexpr();

возможно, следует курить в сторону inline функций

VladimirMalyk ★★★★★
()

И да, лучше глянь асм-вывод. Возможно что при оптимизации что-то нахимичилось.

tia
()
Ответ на: комментарий от arhibot

Вот оно что, спасибо. Надо, значит, книжку эту прочесть.

wieker ★★
() автор топика
Ответ на: комментарий от dimon555

У человека цель выолнить term, а затем ostexpr. При этом только в таком порядке, как я понял. А затем сложить из результаты при этом поменяв знак у результата term.

tia
()
Ответ на: комментарий от wieker

Оффтоп конечно, но зачем в этой функции рекурсия? Может вместо нее бесконечный цикл с условием выхода использовать?

jerry
()
Ответ на: комментарий от jerry

Если это кусок чего-то типа рекурсивного спуска по Шеню, то развертка его в циклы вряд ли очень тривиальна.

metar ★★★
()
Ответ на: комментарий от metar

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

jerry
()
Ответ на: комментарий от jerry

Там как минимум curr_tok глобально объявлен.

anonymous
()
Ответ на: комментарий от jerry

>Оффтоп конечно, но зачем в этой функции рекурсия?

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

wieker ★★
() автор топика

у тебя term изменяет данные, которые потом нужны ostexpr ?
это очень плохая практика!! можно даже сказать г#вн#-код

лучше сделай по аналогии, как делает getopts:
get_token возвращает то, что ты сейчас будешь обрабатывать в данный момент и при этом он инкрементирует внутренние переменные для дальнейших уровней рекурсии

xydo ★★
()
Ответ на: комментарий от arhibot

>В языке «C», как и в большинстве языков, не фиксируется
порядок вычисления операндов в операторе

А я снова спрошу - что тогда обозначает левоассоциативность сложения, заявленная в стандарте? И почему в таблицах с указанием порядка вычислений для сложения пишут «left-to-right»?

KRoN73 ★★★★★
()
Ответ на: комментарий от Dimanc

>Вы знаете, в каком году была написана эта книга?

Боюсь, что языков с фиксированным порядком вычисления операндов было большинство во все времена, начиная с ФОРТРАНа :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

> А я снова спрошу - что тогда обозначает левоассоциативность сложения, заявленная в стандарте?

о_О

Первый раз в первый класс?

Означает, что последовательность сложений будет выполнена с лево на право.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

>Означает, что последовательность сложений будет выполнена с лево на право.

Прекрасно! А какой смысл в указании такой последовательности, если порядок вычисления аргументов не определён?

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Итак, по определению, * является правоассоциативной операцией, если
a * b * c = a * (b * c), и является левоассоциативной операцией, если
a * b * c = (a * b) * c.


Например, + может быть объявлен как право так и левоассоциативной операцией, поскольку a + b + c = a + (b + c) = (a + b) + c. Операция - является левоассоциативной операцией, поскольку a - b - c = (a - b) - c != a - (b - c).


Унарный - в свою очередь правоассоциативная операция, поскольку - - a = - (- a) != (- -) a. Последнее выражение даже не имеет смысла.


Таким образом, мы видим, что ассоциативность операций необходима для правильной интерпретации выражения. Но посмотрите, пусть операция * является для определенности левоассоциативной


a * b * c = (a * b) * c.


Означает ли это, что в выражении x = a + b вначале будет вычислено значение a, потом b и потом выполнено сложение и присвоение? На самом деле, левоассоциативность тут ни при чем. Более того, рассмотрим следующий пример x = a() + b() + c().


Левоассоциативность операции + означает, что выражение будет проинтерпретировано так x = (a() + b()) + c(). Каждая из двух следующих последовательностей выполнения реализует эту интерпретацию

такая

[code=c]
t1 = a();
t2 = b();
t3 = t1 + t2;
t4 = c();
t5 = t3 + t4;
x = t5;
[/code]

и вот такая

[code=c]
t1 = c();
t2 = b();
t3 = a();
t4 = t2 + t3;
t5 = t4 + t1;
x = t5;
[/code]


Левая ассоциативность лишь утверждает, что следующие последовательности выполнений недопустимы

[code=c]
t1 = c();
t2 = b();
t3 = a();
t4 = t1 + t2; t5 = t4 + t3; /* недопустимо */
t4 = t1 + t3; t5 = t4 + t2; /* недопустимо */
x = t5
[/code]


Но как видите о порядке вызовов a(), b(), и c() левая ассоциативность ничего нам не говорит.


Теперь о рекурсивном спуске. Конечно никакой это не **внокод, как кто-то там выразился. Проблема здесь в том, что a(), b() и c() как минимум меняют состояние входного потока, а значит порядок их вызова важен. В этой технике нет ничего плохого. Кстати, синтаксический анализ методом рекурсивного спуска был известен задолго до Шеня, поэтому называть технику Шеневской некорректно.

anonymous
()
Ответ на: комментарий от KRoN73

Ололо.

1. Операторы ассоциативны в рамках одного и того же уровня приоритета.
2. Приведение типов.

LamerOk ★★★★★
()
Ответ на: комментарий от xydo

Вот вот, именно об этом я и вел речь. Вот к вашему предожению иллюстрация, которую мне пришлось реализовывать однажды

Рассмотрим грамматику, например, однозначную грамматику, некоторым образом связанную с форматом EDIFACT (надоели уже арифметические выражения?).

M -> M S ' | S '
S -> S + C | C
C -> C : E | E
E -> WORD

Преобразуем ее вначале для анализа методом рекурсивного спуска.

M -> S ' Mr
Mr -> S ' Mr | /* nothing */
S -> C Sr
Sr -> + C Sr | /* nothing */
C -> E Cr
Cr -> : E Cr | /* nothing */
E -> WORD


M-стартовый символ, WORD это единственный терминальный символ грамматики, означающий произвольную строку символов, не содержащую символов ' + :

Вот интерфейс синтаксического анализатора методом рекурсивного спуска для этой грамматики

pasreM()
parseMr()
parseS()
parseSr()
parseC()
parseCr()
parseE()

Преимущество анализатора методом рекурсивного спуска в простоте его реализации, т.е. в простоте реализации такого интерфейса.

Часто важной задачей является не сам разбор файла, а посторение функции get_next_C(), возвращающей дерево разбора для очередного C из входного потока.

Когда интерфейс синтаксического анализатора, работающего методом рекурсивного спуска есть, становится очевидно, как реализовать такую функцию поверх него.

Попытка реализовать функцию get_next_C() с нуля, без какого-либо нижележащего классического интерфейса синтаксического анализатора, это гораздо бОльший грех разработчика, поскольку идиоматическая задача решается не идиоматическим методом. Ситуация усугубляется тем обстоятельством, что задача эта нетривиальна.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.