使用我的意思是它在许多计算器中使用,如HP35-
我的猜测(和混淆)是 -
可以问这个问题的另一个方法是后缀表示法优于前缀的优点是什么? 谁能开导我?
答案 0 :(得分:5)
一方面,实施评估更容易。
使用前缀,如果您按下运算符,然后是其操作数,则需要具有运算符何时具有其所有操作数的前向知识。基本上你需要跟踪你推动的操作符何时具有所有操作数,以便你可以展开堆栈并进行评估。
由于复杂表达式可能最终会出现堆栈中的许多运算符,因此您需要具有可以处理此问题的数据结构。
例如,这个表达式:- + 10 20 + 30 40
将同时在堆栈上有一个-
和一个+
,对于每个表达式,您需要知道是否有可用的操作数
使用后缀,当您按下运算符时,操作数(应该)已经在堆栈上,只需弹出操作数并进行求值。您只需要一个可以处理操作数的堆栈,而不需要其他数据结构。
答案 1 :(得分:2)
基本上,因为如果你在 postfix 中编写表达式,你可以只使用 Stack 评估该表达式:
阅读表达式的下一个元素
如果是操作数,请进入Stack,
否则从操作所需的堆栈操作数中读取,&将结果推入Stack。
如果不是表达式的结尾,请转到1.
示例强>
expression = 1 2 + 3 4 + *
stack = [ ]
Read 1, 1 is Operand, Push 1
[ 1 ]
Read 2, 2 is Operand, Push 2
[ 1 2 ]
Read +, + is Operation, Pop two Operands 1 2
Evaluate 1 + 2 = 3, Push 3
[ 3 ]
Read 3, 3 is Operand, Push 3
[ 3 3 ]
Read 4, 4 is Operand, Push 4
[ 3 3 4 ]
Read +, + is Operation, Pop two Operands 3 4
Evaluate 3 + 4 = 7, Push 7
[ 3 7 ]
Read *, * is Operation, Pop two Operands 3 7
Evaluate 3 * 7 = 21, Push 21
[ 21 ]
答案 2 :(得分:2)
如果您喜欢人类阅读顺序以匹配机器的基于堆栈的评估顺序,那么postfix是一个不错的选择。
也就是说,假设你从左到右阅读,并非每个人都这样做(例如希伯来语,阿拉伯语......)。并假设您的机器使用堆栈进行评估,而这并非全部(例如,术语重写 - 请参阅Joy)。
另一方面,当机器评估“从前到后/从下到上”时,人类偏好前缀没有任何问题。如果关注是作为令牌到达,序列化也可以反转。工具辅助可以在前缀表示法中更好地工作(首先知道函数/单词可以帮助确定范围有效参数),但是你总是可以从右到左输入。
这只是一个我相信的惯例......
答案 3 :(得分:2)
前缀表示法可能更常用于...在数学中,在F(x,y)等表达式中。这是一个非常古老的惯例,但与许多旧系统(英尺和英寸,信纸)一样,与我们使用设计更加精心的系统时相比,它有缺点。
几乎每一年大学数学教科书都要浪费一页,至少解释f(g(x))
意味着我们首先应用g
然后f
。按阅读顺序执行此操作更有意义:x.f.g
表示我们首先应用f
。然后,如果我们想在"之后应用h
"我们只是说x.f.g.h
。
作为一个例子,考虑一下我最近必须处理的3d旋转问题。我们想根据XYZ惯例旋转矢量。在后缀中,操作为vec.rotx(phi).roty(theta).rotz(psi)
。使用前缀时,我们必须重载*
或()
,然后反转操作的顺序,例如rotz*roty*rotx*vec
。当你想要考虑更大的问题时,不得不一直考虑这一点,这是容易出错和烦人的。
例如,我在其他人的代码中看到了类似rotx*roty*rotz*vec
的内容,我不知道这是一个错误还是一个不寻常的ZYX轮换约定。我还是不知道。该程序有效,因此内部是自洽的,但在这种情况下,前缀表示法很难维护。
前缀表示法的另一个问题是,当我们(或计算机)解析表达式f(g(h(x)))
时,我们必须在我们的内存中(或在堆栈中)保留f
,然后g
,然后h
,然后确定我们可以将h
应用于x
,然后我们可以将g
应用于结果,然后f
应用于结果。与x.f.g.h
相比,内存中的内容太多了。在某些时候(人类比计算机早得多)我们将耗尽内存。以这种方式失败并不常见,但是当x.f.g.h
不需要短期记忆时,为什么要打开门呢?这就像递归和循环之间的区别。
还有一件事:f(g(h(x)))
有很多括号,它开始看起来像Lisp。对于运算符优先级,后缀表示法是明确的。
一些数学家(特别是Nathan Jacobson)试图改变惯例,因为后缀在非交换代数中更容易使用,其中顺序真的很重要,但没什么用。但是既然我们有机会在计算机上做更好的事情,我们应该抓住机会。
答案 4 :(得分:0)
可以通过评估前缀符号 right-to-left
来完成。
- 7 + 2 3
# evaluate + 2 3
- 7 5
# evaluate - 7 5
2
与评估后缀符号 left-to-right
相同。
7 2 3 + -
# put 7 on stack
7 2 3 + -
# evaluate 2 3 +
7 5 -
# evaluate 7 5 -
2
可以通过评估前缀符号 left-to-right
来完成。
|| 1 < 2 3
# put || in instruction stack, 1 in operand stack or keep the pair in stack
instruction-stack: or
operand-stack: 1
< 2 3
# push < 2 3 in stack
instruction-stack: or, less_than
operand-stack: 1, 2, 3
# evaluate < 2 3 as 1
instruction-stack: or
operand-stack: 1, 1
# evaluate || 1 1 as 1
operand-stack:1
请注意,我们可以轻松地对此处的boolean
表达式进行短路优化()(与之前的评估顺序相比)。
|| 1 < 2 3
# put || in instruction stack, 1 in operand stack or keep the pair in stack
instruction-stack: or
operand-stack: 1
< 2 3
# Is it possible to evaluate `|| 1` without evaluating the rest ? Yes !!
# skip < 2 3 and put place-holder 0
instruction-stack: or
operand-stack: 1 0
# evaluate || 1 0 as 1
operand-stack: 1
与评估后缀符号 right-to-left
相同。
可以通过评估前缀符号 left-to-right
来完成。
|| 1 < 2 3
# put || 1 in tuple-stack
stack tuple[or,1,unknown]
< 2 3
# We do not need to compute < 2 3
stack tuple[or,1,unknown]
# evaluate || 1 unknown as 1
1
与评估后缀符号 right-to-left
相同。
在计算器中放置数字时,可以立即计算后缀符号 2 3 +
,而无需了解人类将要放置的符号。前缀表示法与之相反,因为当我们拥有- 7 +
时,我们什么都不做,直到我们得到- 7 + 2 3
之类的东西。
现在前缀符号可以立即求值+ 2 3
,而后缀符号具有3 + -
时,则等待进一步的输入。 / p>
请参阅@AshleyF,请注意,阿拉伯语是从右向左书写的,而英语是从左向书写的书写!
我想 little-endian 和 big-endian 与此前缀/后缀表示法有关。
最后一条评论是,反向抛光符号得到Dijkstra的大力支持(他是短路优化的坚决反对者,被认为是反向抛光符号的发明者)。您可以选择是否支持他的意见(我不支持)。