2014-03-13 1 views
2

В Thinking in Postscript (PDF), Глава 5, упражнения 3 (. С. 64-65) просит читателя реорганизовать этот код, чтобы не храним никаких словарных статей:Что делает LOCAL в Postscript?

36 750 moveto /Times-Roman 24 selectfont 
% works like “show”, leaving current point at proper location 
/ushow 
% linethickness lineposition (words) ushow - 
{ %def 
    LOCAL begin 
    /text exch def 
    /linepos exch def 
    /linethick exch def 
    gsave 
     0 linepos rmoveto 
     text stringwidth rlineto 
     linethick setlinewidth stroke 
    grestore 
    text show 
    end 
} dup 0 4 dict put def 
0.5 -4 (test underlined text) ushow 

Мой вопрос о LOCAL. Ghostscript работает этот код без ошибок, и все же LOCAL не является:

  • Определено в упражнении
  • документированной в приписка Language Reference, третье издание
  • Документально в PostScript Language Tutorial и поваренной

Что, в PostScript, LOCAL?

ответ

3

Ничего не определено. Код немного подлый, так как код dup 0 4 dict put def выполнит исполняемый файл и заменит LOCAL результатом 4 dict. Исполняемый блок (материал между {}) копируется главным образом потому, что put ничего не возвращает. Так как его ссылка на тот же блок слева над с

/ushow {-dict- begin ...rest of the executable...} def 

Это все действует, потому что местные никогда не используется в любом месте (он разрушается до его использовали). Не важно, что вы используете вместо LOCAL.

+0

Я понятия не имел, что программа PS может или может изменить исполняемый массив. Это непростительно. Отличный ответ, спасибо! –

+0

Эй, подожди. Это означает, что PostScript похож на LISP: _Code - это просто данные, которые вы выполняете. И манипулируйте, если хотите. –

+1

@WayneConrad Да – joojaa

2

Как правильно объясняет joojaa, LOCAL не определяется ничем, что нормально, поскольку он заменен перед выполнением. Он анализирует исполняемое имя во время построения тела процедуры (массива), и его использование здесь - это просто выделить слот в массиве. Это может быть любой тип, и я часто видел (и писал) { 0 begin ...} с той же целью. Использование имени позволяет предоставить более семантическую информацию для читателя, читающего код. Я также видел, что это написано { DICT begin ... }Here в моих матричных функциях, я назвал его STATICDICT, по-видимому.

Существует соглашение об использовании всех верхних регистров для любых мета-синтаксических таких жетонов. Это токен типа natetype, но мета-синтаксически он ссылается на объект dicttype, который будет заполнен позже. Нет необходимости (и даже какого-либо механизма) заявлять, что вы делаете в интересах переводчика, но многое можно получить, предпочтя DICT за 0. Опять же, поскольку он будет полностью заменен, вы также можете использовать буквальное имя /LOCAL, чтобы попытаться, idunno, освободить следующий noob, чтобы прочитать ваш код из wild-goose-chase в поисках того, где определено LOCAL ?? С этой целью я также написал просто DUMMY для токена для заполнения в конце. Я полагаю, что выбор среди этих терминов является вопросом стиля или аудитории или каким-то другим нематериальным качеством. вздох ... или просто вопрос контекста.

Существует еще один стиль, который хорошо подходит для создания динамических замещений в процедурных телах. Размещая словарь на dictstack и назвав его (внутри себя, так что это замкнутое пространство имен), мы можем обратиться к нему с //immediate именем

4 dict begin 
/mydict currentdict def 
/proc { 
    //mydict begin 
    ... 
    end 
} 

, а затем удалить словарь перед определением.

end def 

Таким образом, определение порядка обычно (в словаре внешнего уровня (неименованный здесь, по-видимому userdict)), но со словарем встроенным по имени, от того, был доступен под этим именем в то время как тело процедуры было отсканированы.

Это может быть распространено на большее количество процедур, использующих один и тот же частный словарь, путем подтасовки dict из стека для каждого определения.

/enddefbegin { currentdict 3 1 roll end def begin } def 

4 dict begin 
/mydict currentdict def 

/proc1 { 
    //mydict begin 
    ... 
    end 
} enddefbegin 

/proc2 { 
    //mydict begin 
    ... 
    end 
} enddefbegin 

end 

enddefbegin end в конце, конечно, может быть упрощено до end def.

Одно предостережение. Созданный таким образом словарь рекурсивно содержится с собой. Не пытайтесь распечатать его с помощью оператора ghostscript ===!

1

Pg 133 «Blue Book» имеет немного более легкий пример той же методике:

/sampleproc 
{ 0 begin 
    /localvariable 6 def 
    end 
} def 
/sampleproc load 0 1 dict put 

Здесь процедура определяется прежде чем он будет изменен. Немного легче обдумать. В исходном сообщении самая сложная часть для меня была «dup», потому что я не понимал, что массив в стеке не является массивом точно, это ссылка на массив (я думал о копировании по значению, он копирует функции -by-reference), поэтому «put» в исходном коде влияет на массив с первой ссылкой (которая, следовательно, потребляется из стека), а вторая используется для определения процедуры. Это была ошибка Новичок, но, возможно, другие новички могут учиться у него:

Stack Progression:

...            % fast forward 
1. ushow --array-- --array-- 0 4 dict | put def % dict creates a dictionary 
2. ushow --array-- --array-- 0 --dict-- | put def % put arrives on stack 
3. ushow --array-- --array-- 0 --dict-- put | def % put consumes the array 0 and dict 
4. ushow --array-- | def       % def arrives on stack 
5. ushow --array-- def       % def consumes the remaining tokens 
6. 

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