2

我在 PostScript 中有以下程序,我很难理解。

/kochR
 {
   2 copy ge {dup 0 rlineto}
     {
       3 div
       2 copy kochR
       60 rotate
       2 copy kochR
       -120 rotate
       2 copy kochR
       60 rotate
       2  copy kochR
      } ifelse
     pop pop
  } def

0 0 moveto
27 81 kochR
0 27 moveto
9 81 kochR
0 54 moveto
3 81 kochR
0 81 moveto
1 81 kochR
stroke

我对上述程序的问题是:

  1. 这里是什么2 copy ge { dup 0 rlineto }意思?
  2. 这里是如何ifelse工作的,条件是什么?
  3. 在这里做什么3 div
  4. 2 copy KochR语句在这里执行什么?
4

3 回答 3

1
What does 2 copy ge { dup 0 rlineto } mean here?

作为iforifelse运算符的 THEN 子句,它表示“if (stack(top-1) > stack(top)) draw_horizo​​ntal_line((current_x, current_y) -> (current_x + stack(top), current_y)。{ dup 0 rlineto }是递归的闭包:决定何时停止以及做什么而不是递归的部分。rlineto绘制一条相对线。相对于 currentpoint,也就是说。currentpoint 是最后一个路径构造运算符(如moveto, lineto, 但是not rotate, and not stroke) 离开了它。

How does ifelse work here and what is the condition?

ifelse总是以相同的方式工作: booleantype procedure-body procedure-body ifelse:如果布尔值为真,则执行第一个过程主体,否则执行第二个。gt条件是应用于堆栈上 2 个数字的运算符的布尔值结果。由于gt消耗了它的参数,前置2 copy意味着数据不会丢失gt

What does 3 div do here?

由于第二个参数(堆栈顶部)控制图形的整体大小,因此它还控制由所有子调用的组合绘图命令表示的部分图形的“大小”。3 div意味着在每个递归级别,图形的“大小”小于其父级的“大小”,小 1/3。在叶子调用中,条件 a >= b 成立,b 用作组成图像的各个线段的长度。这意味着 a 本身不是行长,而是一个阈值。一旦 b 在下降到 b/3、b/9、b/27、b/81 的过程中遇到或超过阈值 a,那么就该关闭克隆机并让每个人拿起铅笔。

What does the 2 copy KochR statement perform here?

此行执行对 kochR 过程的递归调用,使用相同的阈值和从传递给当前调用的两个参数中减小的大小。使用2 copy意味着这两个值在堆栈上持续存在,直到pop pop进一步下降。


这是对 C 的粗略翻译,假设有一个可用的图形库实现了 Adob​​e 图像模型(也称为 Stencil-Mask 或 Path-Paint 模型)。参数似乎分别是线段的大小和图形的整体大小。因此,最大递归级别由等式 a >= b * (1/3)^R 间接控制。

void kochR(double a, double b) {
    if (a >= b) {
        // line from currentpoint to currentpoint+(b,0)
        // ie. line of length b along current x-axis
        rlineto(b, 0);
    } else {
        b /= 3;
        kochR(a, b); // recurse with reduced length
        rotate(60);  // rotate x-axis CCW by 60 degrees
        kochR(a, b);
        rotate(-120); // rotate x-axis CW by 120 degrees
        kochR(a, b);
        rotate(60);
        kochR(a, b);
    }
}

int main(void) {
    moveto(0,0);
    kochR(27, 81);
    moveto(0, 27);
    kochR(9, 81);
    moveto(0, 54);
    kochR(3, 81);
    moveto(0, 81);
    kochR(1, 81);
    stroke();
}

所以你应该能够从中看出,所有的2 copy东西都是后记必须“保持活力”2个未命名变量的一种手段。每行对应一个过程调用,该调用消耗堆栈中的 2 个变量。如果您在最后一个“程序调用”中pop pop也省略了 final ,您应该能够看到 final是不必要的。2 copypostscript 编程的角度来看,这都是非常基本的东西。但是递归有界的方式非常复杂。

顺便说一句,如果你喜欢这些分形(我喜欢),你绝对必须看看http://en.wikipedia.org/wiki/L-system。太奇妙了。

Cairo Graphics是一个流行的实现 Adob​​e 图像模型的 C 库,但我将把创建工作程序的任务留给读者作为练习。:)

于 2012-09-12T16:03:48.717 回答
1

您似乎在相当基本的 PostScript 概念和操作方面遇到了一些问题,您有 PostScript 语言参考手册的副本吗?

  1. copy 复制操作数堆栈上的条目,前面的参数告诉解释器要复制堆栈上的操作数。ge 测试大于或等于,然后是一个可执行数组。

  2. ifelse 以您期望的方式工作,如果条件为真,则执行第一个可执行数组,否则执行第二个可执行数组。

  3. 3 div 将堆栈上的操作数除以 3,并将结果放在操作数堆栈上。

  4. 2 副本与第 1 点中的相同,“KochR”是一个命名对象,在这种情况下它是一个可执行数组。它必须是先前定义的,否则会出现“未定义”错误。

于 2012-09-12T07:30:42.670 回答
1

2 copy gecopy的KochR2 个参数(我假设它是一个坐标对)并比较它的分量,得到一个真值。然后ifelse根据该真值决定是否执行{ dup 0 rlineto }或其他块中的语句。 3 div将坐标对的 y 值一分为三,使其沿该轴更接近原点。 2 copy KochR创建坐标对的副本(将 y 切成三分之二,或者将其位置旋转),然后递归地使用它们对它们执行相同的操作。

我对该函数所做的最佳估计是,它从当前点沿传递给 的点的方向绘制一个衰减的锯齿形,将点KochR列表留在操作数堆栈上。该程序将几个这样的锯齿形附加到当前路径,每个都是自己的子路径,然后将它们全部描边,将整个点列表留在堆栈上(一个可能的问题)。

于 2012-09-13T15:15:04.080 回答