我无法理解联机帮助页中给出的示例dc
:
$ dc
1 0:a 0Sa 2 0:a La 0;ap
1
对我来说答案应该是 2 因为:
1 0:a
这里我们将 1 存储在数组的第 0 个位置a
。0Sa
现在我们将 0 压入寄存器堆栈a
。2 0:a
现在,我们再次将 2 存储在数组的第 0 个位置,a
从而覆盖该位置存储的前一个 1。La
现在我们弹出寄存器堆栈中存储的 0a
并将其推送到主堆栈。0;a
现在我们再次将 0 压入主堆栈,然后将其弹出以用作数组索引,因此我们将存储在数组第 0 个位置的 2 压a
入主堆栈。p
现在我们打印主堆栈的顶部,即 2。所以答案应该是 2。
我缺少什么?
PS-我想用作dc
标签,但看起来它不存在,并且必须使用至少一个如此使用的标签debian
(我的工作站操作系统)。
答案1
就像混合数组和堆栈一样。在示例中,寄存器a
既用作数组,也用作堆栈。
1 0:a
0 Sa
2 0:a
La
0;a p
- 首先
:a
-注册A被视为大批。 - 然后
Sa
-注册A被视为堆栈。实际上,将数组从 pt 1. 向下推并创建一个新的大批。正如man
:请注意,寄存器的每个堆栈实例都有其自己的关联数组。 - 然后
:a
-注册A被视为大批。将先前值和第一个数组值向下推。 - 然后
La
-注册A被视为堆。为了达到拳头堆叠数组丢弃它,a[0]=2
因为它是一个数组。 - 然后
;a
-注册A被视为大批。现在只剩下一个值,第一个数组值添加到A这是1
.
请参阅答案底部的更多示例。
根据评论:
«每个寄存器有多少个数组和堆栈?我以为一个寄存器有一个堆栈和一个单独的数组。»
堆栈:
堆栈输入dc
是下推堆栈或 LIFO(后进先出)。和餐厅里的盘子一样。
,------ pop / push - take / leave
/
|
v
[-----] top
[-----] ... ... .
[-----] [-----] [-----] ..
(main) (register-a) (register-b) ...
我们有一个主要的堆栈或工作除非指定了需要寄存器的操作,否则使用堆栈。每个寄存器都有自己的堆栈。
基本的登记运营:
Sr
:流行一价值来自主要的堆栈和推它到由寄存器指定的堆栈上r
。两个堆栈均已修改。Lr
:流行一r
由和指定的寄存器堆栈中的值推它到主要的堆。两个堆栈均已修改。sr
:流行一价值来自主要的堆栈和写就可以注册了r
。实际上更改由寄存器指定的堆栈中的最高值r
。如果该堆栈中没有值 - 添加它。主要的堆栈已修改。寄存器堆栈保留在更改值旁边。lr
:读来自寄存器的值r
。实际上,如果有多个值,则为最上面的值。主要的改变了。寄存器堆栈保存下来。:r
:弹出两个值来自主要的堆栈并使用第一个(最顶层)作为在寄存器中存储数组中第二个值的索引r
。主要的改变了。寄存器堆栈保存下来。;r
:流行一价值来自主要的堆栈并将其用作从 指定的寄存器中的当前数组中读取的位置的索引r
。主要的改变了。寄存器堆栈保存下来。
堆栈和数组混合在一起
一种观察方法是成对观察。当你开始时,所有寄存器都是空的。当您通过 向堆栈添加一个元素时Sr
,您隐藏该堆栈中的任何底层元素。假设你这样做:
1 Sx
2 Sx
4 Sx
x = (S) 4 VISIBLE
(S) 2 HIDDEN
(S) 1 HIDDEN
现在你可以改变寄存器中的值X, 那是;更改最上面的元素 by sx
,您可以读取lx
– 而不改变堆栈中元素的数量:
lx p # Read value in register x - in effect read topmost element from stack.
4 # Printed value by p.
3 sx # Change value in register x - in effect change topmost element in stack.
x = (S) 3 VISIBLE
(S) 2 HIDDEN
(S) 1 HIDDEN
如果您决定添加数组元素,事情就会开始朝着更复杂的方向发展。
4 1:x
5 2:x
x = [A]
[2]=5 VISIBLE
[1]=4 VISIBLE
(S) 3 VISIBLE
(S) 2 HIDDEN
(S) 1 HIDDEN
现在我们已经为当前的栈中的数组。我们可以读取和修改任何可见的元素。
44 1:x
55 2:x
33 sx
1;x p # Read and print index 1
44
lx p # Read and print stack top.
33
x = [A]
[2]=55 VISIBLE
[1]=44 VISIBLE
(S) 33 VISIBLE
(S) 2 HIDDEN
(S) 1 HIDDEN
如果我们那么添加堆栈元素的一种方式可以说栈帧是不可扩展的,因为我们已经向其上方的数组添加了值。因此一个新的栈帧被添加。
6 Sx
7 Sx
x = (S) 7 VISIBLE
(S) 6 HIDDEN
[A]
[2]=55 HIDDEN
[1]=44 HIDDEN
(S) 33 HIDDEN
(S) 2 HIDDEN
(S) 1 HIDDEN
如果我们现在尝试访问最后的数组它是隐藏的。实际上,我们从一个空数组中读取,结果是默认值0
。我们可以修改寄存器值7by sr
,但不能访问下两层的数组,除非我们删除上面的两个堆栈元素。
如果我们现在决定添加一些数组元素,它们将被添加到与顶部堆栈元素定位(作为配对数组)的新数组中。
8 1:x
9 2:x
x = [A]
[2]=9 VISIBLE
[1]=8 VISIBLE
(S) 7 VISIBLE
(S) 6 HIDDEN
[A]
[2]=55 HIDDEN
[1]=44 HIDDEN
(S) 33 HIDDEN
(S) 2 HIDDEN
(S) 1 HIDDEN
现在如果我们做一个流行音乐我们弹出的堆栈7但因为有一个数组之间(可以这么说)它也被删除了。
Lx p # Pop + print top stack element.
7 # Value printed.
x = (S) 6 VISIBLE
[A]
[2]=55 HIDDEN
[1]=44 HIDDEN
(S) 33 HIDDEN
(S) 2 HIDDEN
(S) 1 HIDDEN
数组与8和9离开了。有值的栈元素6是可见的。但底层数组被阻塞了。按1;x p
产量读取0。
在某种程度上,我们可以说堆栈元素是阻塞的,而数组是不透明的。数组有点像挂在到堆栈元素。
我们还需要做另一件事流行音乐来自堆显示底层堆栈元素+数组。
Lx p # Pop + print top stack element.
6 # Value printed.
x = [A]
[2]=55 VISIBLE
[1]=44 VISIBLE
(S) 33 VISIBLE
(S) 2 HIDDEN
(S) 1 HIDDEN
总之,可以说数组和堆栈的数量不限于每个寄存器一个。
–每个寄存器有多少个数组和堆栈?
– 取决于您对该寄存器执行了多少次交替Sr
和操作。:r
另一种看待它的方式是只有一个堆栈但有多个数组当且仅当我们在添加数组元素之间添加堆栈元素...
还有另一种说法是,在任何给定时刻,当前数组都不是
[register][array]
但
[register][stack-element][array]
这使:
[register][stack-element][array][...]
[register][stack-element][array][1]
[register][stack-element][array][0]
并且该stack-element
部分是不透明的、只读的等。尽管在这种情况下我们也必须记住我们不需要堆栈元素的值。仅将数组值添加到寄存器即可。
或者每个堆栈元素都与我们可以修改的零填充数组配对:
1 Sx
2 Sx
3 Sx
4 1:x
5 2:x
6 Sx
7 Sx
8 1:x
9 2:x
x = (S) 7 + A[0]=0 A[1]=8 A[2]=9 A[3]=0 ... A[2048]=0
(S) 6 + A[0]=0 ... A[2048]=0
(S) 33 + A[0]=0 A[1]=4 A[2]=5 A[3]=0 ... A[2048]=0
(S) 2 + A[0]=0 ... A[2048]=0
(S) 1 + A[0]=0 ... A[2048]=0
希望它能更清楚一点。
一些例子
$ dc
[ein] 0:a
[zwei] Sa
[drei] 0:a
0;ap # Copy + print index 0 of topmost array in register a
drei
Lap # Throws away [drei] and pops+prints first element in stack*
zwei
0;ap # Copy + print index 0 of first array
ein
$ dc
[uno] 0:a # Array a(1)
[dos] 1:a # Array a(1)
[tres] 2:a # Array a(1)
[cuatro] Sa # Array a(2)
[cinco] Sa # Array a(2)
[seis] Sa # Array a(2)
[siete] 0:a # Array a(3)
[ocho] 1:a # Array a(3)
[nueve] 2:a # Array a(3)
Laf # Throws away Array 3 to get to first stack array, Array 2.
seis
Laf
cinco
seis
Laf
cuatro
cinco
seis
2;af # Now we're at the first array, Array 1.
tres
cuatro
cinco
seis
1;af
dos
tres
cuatro
cinco
seis
0;af
uno
dos
tres
cuatro
cinco
seis
答案2
你必须记住,这dc
是一个编译器——而且是一个疯狂的旧编译器。它是一种机器语言——面向堆栈的计算器。它非常强大,但它的界面并不是为您设计的 - 它的设计目的是在所有用户友好性都经过机器处理后,有效地处理您用其他语言编写的指令。
dc
不以直观的方式存储数据。dc
东西东西。当它读取新输入时,如果不立即执行,则该值只会被扔到堆栈上。堆栈中的所有内容都已被向下推 - 最新的位于顶部,最旧的位于底部。您必须处理最新的堆栈成员才能检索最旧的堆栈成员。
很多人都能得到这么多。毕竟,堆栈并不那么奇怪——它有点像洗衣篮。但这也是相当一维的,而且dc
比这更进一步。
所以就有了输入堆栈——主堆栈。默认情况下,这是所有dc
指令输入和输出的所在。当你向它发出命令时,它就会工作。但还有所有其他寄存器 - 至少 256 个。其中每一个本身也是一个堆栈。
[Ss]
您通常使用ave 和[Ll]
oad 命令来处理寄存器。要将主堆栈上的顶部值作为标量值保存到寄存器中,a
请执行以下操作sa
。然后,您可以l
随时使用 将此标量值加载回主堆栈的顶部la
。好吧,只要寄存器的当前实例a
保持当前状态,就可以。
创建一个新的a
顶部寄存器的标量实例老的- 在标量值保留的地方 - 您可以使用Sa
.该命令弹出主堆栈并入栈寄存器a
堆栈。与 不同la
,该La
命令是寄存器的破坏性L
负载 - 当使用该命令将标量值弹出到主堆栈时,该寄存器实例中的任何内容都会被破坏,并且寄存器的任何先前实例都可以再次访问。
这也很容易通过一点练习就能掌握——它就像主堆栈一样,但每个寄存器都有一个。但每个寄存器还有一个更进一步的维度——它们的数组。
每个寄存器的每个实例都有一个数组。我认为默认大小是每个 2048 个索引 - 尽管我经常想知道堆栈有多深并且只能说它相当深。当您实例化寄存器的新标量值时,您不仅会下推其标量值,还会下推其数组。新实例有一个新数组,旧实例的数组保持不变,并且在弹出新实例后将像标量值一样可供您访问。
数组索引访问有点棘手。首先,几乎所有主堆栈操作都是破坏性的。访问数组索引的唯一方法是从主堆栈中提取其值,然后调用它。因为您的索引引用此时已被破坏,所以很难召回它。o
并且O
对于保留索引计数器很有用 - 但不要尝试使用索引 1 或 0。
不管怎样,寄存器的数组和它的堆栈不是独立的,而是相互依赖和多维的。通过练习、耐心和一点超人的决心,可以用它完成一些很酷的事情。祝你好运。
答案3
我遇到了同样的问题,但仔细重新阅读手册页本身中示例周围的文本就足够了:
:r Will pop the top two values off of the stack. The old sec-
ond-to-top value will be stored in the array r, indexed by
the old top-of-stack value.
;r Pops the top-of-stack and uses it as an index into the array
r. The selected value is then pushed onto the stack.
Note that each stacked instance of a register has its own array
associated with it. Thus 1 0:a 0Sa 2 0:a La 0;ap will print 1,
because the 2 was stored in an instance of 0:a that was later
popped.
这里的最后一段完全回答了你的问题。 2 存储在 0:a 的实例中,该实例随后被弹出(当 La 将寄存器 a 中的标量值放入主堆栈时,该实例会默默地被丢弃)。