
\pgf@marshal
我在 PGF 包中遇到了一種明顯慣用的用法,我想知道為什麼要使用它。
例如來自pgflibraryshapes.gates.logic.US.code.tex
:
{%
\edef\pgf@marshal{%
\noexpand\pgfpatharc{90}{-90}{\the\pgf@yc}%
}%
\pgf@marshal%
}
這可能是一個簡單的問題,但為什麼不只是
\pgfpatharc{90}{-90}{\the\pgf@yc}
?
答案1
雖然\pgfpatharc
不使用\pgf@yc
(至少在它已經被評估並存儲在其他地方之前)並且這樣做是安全的
\pgfpatharc{90}{-90}{\pgf@yc}
(沒有\the
偶數),手冊本身有警告
注意力:PGF 使用這些暫存器來執行路徑操作。出於效率的考慮,路徑命令並不總是保護它們。因此,程式碼
\pgfpointadd{\pgfpoint{\pgf@xa}{\pgf@ya}}{\pgfpoint{\pgf@xb}{\pgf@yb}}
可能會失敗:在內部
\pgfpointadd
,\pgf@xa
和 友元暫存器可能會被修改。特別是,可能會發生在評估\pgf@xb
之前發生更改的情況。\pgfpoint{\pgf@xb}{\pgf@yb}
正確的做法是先使用這些值來擴展所有內容\edef
,然後再處理這些值,從而導致不必要的昂貴操作。當然,只需查看原始程式碼以\pgfpointadd
查看使用了哪些寄存器,就可以避免這種情況。
(有趣的是,給定的範例實際上可以工作,但不能
\pgfpointadd{\pgfpoint{\pgf@xb}{\pgf@yb}}{\pgfpoint{\pgf@xa}{\pgf@ya}}
因為第一個點的座標將被儲存\pgf@xa
並\pgf@ya
稍後添加到第二個點的座標中。
在 的情況下\pgfpatharc
, get 在使用#3
之前進行評估,而這只發生在範圍內,因此在 後仍然具有相同的值。\pgf@yc
\pgf@yc
\pgfpatharc
所以,是的,電路庫的開發人員可以查看 的定義,\pgfpatharc
並且可以確定沒有意義。
但還有其他幾點要考慮:
\pgfpatharc
在編寫電路庫時,該命令的工作方式可能有所不同。- 該
\pgfpatharc
命令將來可能會發生變化。 - 電路庫的作者在其他時候注意到了衝突,並更改了所有類似的巨集調用,以便他們永遠不必再次處理此類問題。
答案2
對於那些不太熟悉 TeX 執行模型的人來說,這是另一個視角。
維基詞典:
marshal(動詞)
4. 收集資料以供傳輸。
5.(計算,傳遞)將物件序列化為由位元組序列表示的編組狀態,該狀態稍後可以轉換回具有等效屬性的物件。
雖然在計算環境中通常需要含義 5,但在本例中含義 4 似乎更相關。但它也可以理解為「將程式碼序列化為令牌列表,然後將令牌列表作為程式碼執行」——但是,在 TeX 中,程式碼和令牌列表基本上是相同的東西。
長話短說:
預設情況下,TeX 命令是“從外到內”執行的——也就是說,與典型的程式語言不同,典型的程式語言在“傳遞給函數”之前先“計算”“函數參數”,在TeX中,參數被傳遞給宏觀逐字。
- 在 Python 中,
f(1+2)
和f(3)
是相同的,因為在接收它之前1+2
進行評估。在 TeX 中,和並不相同。3
f
\f{\numexpr 1+2\relax}
\f{3}
- 在 Python 中,
在這種情況下,正如中所解釋的另一個答案,呼叫者希望在傳遞給函數之前儘早評估該數字。
TeX 的處理標記的機制數量非常有限。您看到的程式碼相當於
eval(r"\pgfpatharc{90}{-90}{" + pgfyc + "}")
在 Python 語法中 --- 程式碼作為標記清單進行操作,然後執行。
另一種方法是
\expandafter
在程式碼中的任何地方添加,這肯定效率較低。 (還有\expanded{\unexpanded{...}}
,但在編寫程式碼時還不存在。)使用這個習語的其他地方可以看到,例如如何
pgfplots
在循環中使用繪圖和\edef\temp{...}\temp
。前綴
pgf@
表示巨集名稱是 pgf 套件內部的。如果您不編輯 PGF/TikZ 原始碼,則應該使用自己的名稱而不是重複使用\pgf@marshal
.