这个问题的目的是从编程角度更好地理解PostScript。下面描述的目标只是用于说明的一个例子。
在PostScript语言中,我可以定义一个过程来设置当前图形颜色,如下所示:
/cRED { 1 0 0 setrgbcolor } def % define a procedure to set the color to red.
我想知道是否有办法定义一个定义其他颜色程序的程序。假设定义了一个名为cdef
的过程。我可以按如下方式使用它:
/cRED 1 0 0 cdef
这应该与之前的cRED定义具有相同的效果。
问题是我似乎无法弄清楚如何在传递给def
的过程中“捕获”堆栈上项目的文字值。
我尝试了以下内容:
/cdef { /B exch def /G exch def /R exch def { R G B setrgbcolor } bind def } def
/cRED 1 0 0 cdef
/cGRN 0 1 0 cdef
/cBLU 0 0 1 cdef
我的期望是,通过使用bind
,R
G
和B
的值将被逐字地捕获。即我期待上面的代码等同于:
/cRED { 1 0 0 setrgbcolor } def
/cGRN { 0 1 0 setrgbcolor } def
/cBLU { 0 0 1 setrgbcolor } def
不幸的是,实际结果是cRED
cGRN
和cBLU
都将颜色设置为蓝色。这是因为cRED
cGRN
和cBLU
仍然依赖于R
G
和B
对象(全局变量)。因此,所有人的颜色都是蓝色,因为cBLU
最后定义,设置R
G
和B
。显然bind
没有按照我的预期工作。
有没有办法定义cdef
来实现这个目标?问题的关键是能够从堆栈中弹出一个值并使用def
逐字存储它。例如。像这样的伪代码:
/cdef { { $ $ $ setrgbcolor } bind def } def
在评估cdef时,将$
替换为堆栈顶部项的文字值。因此/cCYN 0 1 1 cdef
被评估为/cCYN { 0 1 1 setrgbcolor } bind def
是否有一些运营商符合上述$
的目的?运算符pop
,=
和index
很接近,但似乎不起作用。
此外,立即评估名称(例如//name
)的使用似乎很有希望,但它们似乎甚至在执行cdef
之前就会被评估。
由于
答案 0 :(得分:4)
通过"滚动"适当的参数而不是分配它们
/cdef { [ 4 1 roll /setrgbcolor load ] cvx bind def } def
这样,当执行] cvx bind def
inside时,它会在操作数堆栈上找到
/YourNameForTheProcedure
[ (i.e. mark)
your three parameters (the mark has been rolled below them)
the setrgbcolor operator (or procedure?)
然后结束]
将从三个数字和setrgbcolor中创建一个数组,该数组将由cvx
生成一个过程。
注意:然后您必须按正确的顺序传递r,g,b参数:
/CR 1 0 0 cdef
/CG 0 1 0 cdef
/CB 0 0 1 cdef
答案 1 :(得分:4)
由于您知道元素的确切数量,因此您可以跳过与标记混淆的堆栈并按以下方式执行:
/cdef { /setrgbcolor load 4 array astore cvx def } def
更复杂的方法可能对更复杂的功能有用。您可以定义参数并将定义替换为字符串模板。我认为这是你用立即评估的名字努力的方向。执行字符串会产生过程主体,但扫描和评估是在运行时执行的。
/cdef {
3 dict begin
{ b g r } { exch def } forall
({ //r //g //b setrgbcolor }) cvx exec
end
def
} def
另一种方式,这可能是笨拙但非常灵活。
/curry {
/exec cvx
3 array astore cvx
} def
/cdef {
{setrgbcolor} 3{curry}repeat
def
} def
这导致此过程被定义为值1 2 3:
{ 1 { 2 { 3 { setrgbcolor } exec } exec } exec }
所以,微观效率可能略有下降。但它可以用于各种各样的事情。