Понимание рекурсивной функции снежинки Коха в Postscript

У меня есть подписка на программу на 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?

person venkysmarty    schedule 12.09.2012    source источник
comment
Один KochR, а все остальные kochR. Вы правильно скопировали код?   -  person Kurt Pfeifle    schedule 12.09.2012
comment
Я проверил ваш код, и в нем действительно есть ошибка. 2 копии KochR должны быть 2 копии kochR (см. K vs. k). - отредактировал.   -  person juFo    schedule 25.04.2017


Ответы (3)


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

В качестве предложения THEN оператора if или ifelse оно означает «if (stack (top-1)> stack (top)) draw_horizontal_line ((current_x, current_y) -> (current_x + stack (top), current_y). Процедура -body { dup 0 rlineto } - это закрытие рекурсии: часть, которая решает, когда остановиться и что делать вместо рекурсии. rlineto рисует относительную линию. относительно текущая точка, то есть текущая точка находится там, где последний оператор построения пути (например, moveto, lineto, но не rotate и не stroke) оставил ее.

How does ifelse work here and what is the condition?

ifelse всегда работает одинаково: логический тип тело процедуры тело процедуры 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, предполагающий наличие доступной графической библиотеки, которая реализует модель изображения Adobe (также называемую моделью 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 материал - это средство, которое Postscript должен «поддерживать» 2 безымянных переменных. Каждая строка соответствует вызову процедуры, которая потребляет 2 переменные из стека. Вы должны увидеть, что последний pop pop будет ненужным, если вы также опустите последний 2 copy в последнем «вызове процедуры». С точки зрения программирования postscript это все довольно простые вещи. Но способ ограничения рекурсии довольно сложен.

Кстати, если вам нравятся такие фракталы (а я люблю), вы обязательно должны увидеть http://en.wikipedia.org/wiki/L-system. Это потрясающе.

Одной из популярных библиотек C, реализующих модель изображений Adobe, является Cairo Graphics, но я оставлю задачу создания рабочей программы. как упражнение для читателя. :)

person luser droog    schedule 12.09.2012

2 copy ge копирует 2 аргумента KochR (я предполагаю, что это пара координат) и сравнивает его компоненты, получая значение истинности. Затем ifelse решает, основываясь на этом значении истинности, делать ли { dup 0 rlineto } или операторы в другом блоке. 3 div делит значение y пары координат на три, делая эту точку намного ближе к началу координат по этой оси. 2 copy KochR создает копию пары координат (у которой либо y разрезано на треть, либо его положение повернуто), которые затем рекурсивно используются для выполнения того же действия с ними.

Моя лучшая оценка того, что делает функция, заключается в том, что она рисует зигзагообразный зигзаг от текущей точки в направлении точки, переданной в KochR, оставляя список точек в стеке операндов. Программа добавляет несколько таких зигзагов к текущему пути, каждый из которых является собственным подпутьем, а затем обводит их все, оставляя весь список точек в стеке (возможная проблема).

person AJMansfield    schedule 13.09.2012

Похоже, у вас проблемы с базовыми концепциями и операциями PostScript. У вас есть справочное руководство по языку PostScript?

  1. copy копирует запись в стек операндов, предыдущий аргумент сообщает интерпретатору, сколько операндов в стеке нужно скопировать. ge проверяет значение больше или равно, а затем следует исполняемый массив.

  2. ifelse работает так, как вы ожидаете, если условие истинно, то выполняется первый исполняемый массив, иначе выполняется второй исполняемый массив.

  3. 3 div делит операнд в стеке на 3 и помещает результат в стек операндов.

  4. 2 copy делает то же самое, что и в пункте 1, «KochR» - это именованный объект, в данном случае это исполняемый массив. Он должен быть определен ранее, иначе возникнет ошибка «undefined».

person KenS    schedule 12.09.2012
comment
Из приведенной выше программы, что делает 2 copy ge {dup 0 rlineto}? Собственно я пытаюсь конвертировать эту программу на C? Я не обычный программист почтовых скриптов. Что здесь 2 стоит? - person venkysmarty; 12.09.2012
comment
В случае, если кому-то еще просто любопытно, сначала верхний регистр K является ошибкой, все должно быть в нижнем регистре. После исправления этого кода отрисовывается фрактальный узор кривой Коха (довольно круто для такого небольшого фрагмента кода ...). Бьюсь об заклад, использование кривой котча в Google на C будет более эффективным, чем изучение постскриптума. - person agentp; 12.09.2012
comment
@venkysmarty: Пожалуйста !! Вы уже задавали вопрос на странице Копировать команду в PostScript. По крайней мере, можно ожидать, что вы прочитаете там ответы. Тогда вы хотя бы знаете, что 2 copy дублирует два самых верхних элемента, которые уже находятся в стеке, и помещает их (еще раз) в стек. - Также было бы неплохо, если бы вы проголосовали за ответы, которые правильно отвечают на ваш вопрос. - person Kurt Pfeifle; 12.09.2012