首先,在为定义选择名称时要非常小心。 length 是操作员的名称。因此,当您的定义生效时(可能在 userdict 中),操作员(在 systemdict 中)不能按名称访问*。
对于任何棘手的堆栈操作代码,在每行末尾编写描述堆栈的 cmets 是一个非常好的习惯。这是您可以使用“免费”变量名的地方。
count % ... n
现在,由于我们立即使用此值,因此实际上根本不需要定义它。把它留在堆栈上。
{ %
} repeat
现在循环记录堆栈可能看起来很棘手,但实际上它是完全相同的。重复循环从堆栈中取出重复计数参数,因此该过程从下面的内容开始。
{ % ...
1 index 0 index 会比1 index 1 index 更好(对吗?因为第一个改变了堆栈深度)。但最好是2 copy。
2 copy gt { % ... x y (x>y)
这里的(x>y) 不在堆栈上,但代表了变量关系的知识。
2 1 roll 比 exch 更好。
exch % ... y x (x>y)
count 1 sub -1 roll %
这会将第二个从底部拉到顶部。请参阅我的roll 操作员指南:Positive j to roll away, negative to retrieve.
exch % a b ... y x (x>y)
但是如果x<y 那么我们仍然想从底部滚动下一个数字,对吗?所以if 子句应该在这里结束。
} if
count 1 sub -1 roll % a ... y x b
如果您删除1 sub,那么它会抓取堆栈的底部。然后我认为它应该按照您的描述进行。
} repeat
组装完毕。
count % ... n
{ % ...
2 copy gt { % a ... x y (x>y)
exch % a ... y x (x>y)
} if % a ... y x (x>y)
count -1 roll % ... y x a (x>y)
} repeat
一天后编辑:嗯。问题。这是不对的。由于roll 发生在比较之后,因此在循环终止之前会有一个额外的不必要的roll,它将较小的被拒绝值之一放在顶部。
快速解决方法是添加
count 1 roll % a ... y x (x>y)
在最后,在循环之后。但我认为更好的方法是先滚动,然后比较。
如果我看到过的话,那就是“围栏张贴”问题。
a b c d e f g h
g>h
f>g
e>f
d>e
c>d
b>c
a>b
所以我们实际上只需要 n-1 个比较。这导致了这个版本,我认为应该更好。
count 1 sub % a b c ... x y n-1
{
count -1 roll % b c ... x y a
2 copy gt { exch } if % b c ... x y a (a>y)
} repeat
还有一个不必要的roll(第一个),但它现在无害了。
[*] 在应用了bind 的过程中仍然可以访问它。