Во-первых, будьте очень осторожны в выборе имен для своих определений. length
- имя оператора. Поэтому, пока ваше определение действует (предположительно в userdict
), оператор (в systemdict
) недоступен по имени *.
Для любого сложного кода манипулирования стеками очень хорошо писать комментарии, описывающие стек в конце каждой строки. Здесь вы можете использовать «свободные» имена переменных.
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
Edit, один день позже: Um. Проблема. Это не правильно. Так как 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
.