我有一个循环,它使用token提取字符串中的每个单词,然后我希望能够像这样对它进行计算:
(%1%2添加%3%4添加)
但是不管你怎么写,我都会得到
7
add
2
1我想让它成为
7
3这就是我正在使用的
{ %loop
pstack
(repl> )print flush
(%lineedit)(r)file
dup bytesavailable string readstring pop
{
token
{}{exit}ifelse
exch
dup () eq {pop exec exit}if
exec
}loop
}loop发布于 2014-03-07 11:00:41
我建议你在每一行的末尾写上堆栈注释。这真的很有帮助。
{ %loop
pstack
(repl> )print flush
(%lineedit)(r)file % f
dup bytesavailable string readstring pop % s
{
token % s t b
{}{exit}ifelse % s t
exch % t s
dup () eq {pop exec exit}if % t s
exec % t
}loop
}loop因此,您将执行剩余的子字符串,而不是令牌。在内部循环中的exec之前,还需要另一个exch。它将执行令牌而不是子字符串。但这样做的一个问题是,字符串位于堆栈上。所以add不会工作,因为它会在堆栈顶部找到一个字符串,而不是下面的数字。
因此,在执行之前按名称保存子字符串,然后在下一次迭代之前将其放回原处可能会更好。
{ % s
token % s t b
{}{exit}ifelse % s t
exch % t s
dup () eq {pop exec exit}if % t s
/rem exch def % t
exec
rem % s
}loop对于postscript的新手来说,这一部分可能更令人困惑,而不是有帮助。继续读下去,后果自负。如果你在中间迷路了,一定要看到最后一个超级简单的技巧。
postscript黑客应该问的下一个问题是:“我怎样才能做到这一点而不用这个rem名称污染名称空间?”
为此,我将使用一个疯狂的技巧,即利用loop操作符创建一个具有额外存储空间的过程体。
{ procedure body } exec
{ procedure body exit extra storage } loop上面的两个构造都将执行procedure body,然后返回控制。但是使用带有显式exit的loop可以让我们将额外的东西放入数组中。
因此,我们从上面获取内部循环。
token{}{exit}ifelse exch dup()eq{pop exec exit}if/rem exch def exec rem将其包装在一个“退出循环”中。
{
token{}{exit}ifelse exch dup()eq{pop exec exit}if/rem exch def exec rem
exit } loop我们将把字符串的余数存储在exit之后。
{
token{}{exit}ifelse exch dup()eq{pop exec exit}if/rem exch def exec rem
exit STR } loop将/name exch def替换为存储到数组中的代码。这个数组将是循环体的一个子数组,它只保存[ STR ]额外的存储空间。
/rem exch def --> ARR exch 0 exch put
rem --> ARR 0 get
{
token{}{exit}ifelse exch dup()eq{pop exec exit}if ARR exch 0 exch put exec ARR 0 get
exit STR } loop这个循环当然是直截了当的:它实际上并没有循环。因此,为了替换上面的内部循环,我们将其包装在另一个循环中。
{ {
token{}{exit}ifelse exch dup()eq{pop exec exit}if ARR exch 0 exch put exec ARR 0 get
exit STR } loop } loop然后,我们需要在代码中ARR所在的位置插入子数组。这是包含(虚拟) STR标记的(内部)内循环的子数组。
% 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
{ { token{}{exit}ifelse exch dup()eq{pop exec exit}if ARR exch 0 exch put exec ARR 0 get exit STR } loop } loop所以我们需要在array10和array16处插入一个子数组20,1。在调用外部作用域中的loop之前,我们可以这样做。
{ {
token{}{exit}ifelse exch dup()eq{pop exec exit}if ARR exch 0 exch put exec ARR 0 get
exit STR } loop }
dup 0 get % loop-body inner-loop get a copy of the exit-loop
dup 20 1 getinterval % loop-body inner-loop [STR] take a subarray of the exit-loop
2 copy 10 exch put % loop-body inner-loop [STR] insert in position 10
16 exch put % loop-body' insert in position 16
loop % call the loop operator在那里,一个没有名字的循环。:)
请注意,我们在代码中仍然保留了虚拟名称STR,这没问题。它将解析为一个名称,并在数组中分配一个额外的插槽。它不需要在任何地方定义,因为它永远不会被执行。
对以上内容的改进。我们真的不需要模板代码中的第二个ARR。我们可以将字符串直接存储到过程数组中需要它的位置。那么我们甚至不需要“退出循环”。因此,模板变成:
% 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
{ token{}{exit}ifelse exch dup()eq{pop exec exit}if ARR exch 0 exch put exec STR } loop{ token{}{exit}ifelse exch dup()eq{pop exec exit}if ARR exch 0 exch put exec STR }
dup dup 10 exch % loop loop 10 loop prepare stack
16 1 getinterval % loop loop 10 [STR] take a subarray
put % loop insert in position 10
loop % call loop operator对改进的改进。我们实际上也不需要子数组。我们可以将整个循环数组存储在ARR位置,并在存储代码中使用索引16而不是0。
{ token not{exit}if exch dup()eq{pop exec exit}if ARR exch 16 exch put exec STR }
dup 10 1 index % loop loop 10 loop prepare stack
put % loop insert in position 10
loop % call loop operator--
这比它需要的要复杂得多。我们可以简单地做一个小数组来对这两件事的执行进行排序。
{exec rem}因此:
{ % s
token % s t b
not{exit}if % s t
exch % t s
dup () eq {pop exec exit}if % t s
/exec cvx exch 2 array astore cvx % t {exec s}
exec
}loop发布于 2014-03-06 16:13:01
Token只是返回堆栈上的“标记化”对象,它不会对它做任何进一步的事情。如果要执行该操作,必须检查返回对象的类型,并对可执行对象执行“exec”。
https://stackoverflow.com/questions/22207532
复制相似问题