PGF 中 \pgf@marshal 的慣用用法

PGF 中 \pgf@marshal 的慣用用法

\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 中,和並不相同。3f\f{\numexpr 1+2\relax}\f{3}
  • 在這種情況下,正如中所解釋的另一個答案,呼叫者希望在傳遞給函數之前儘早評估該數字。

  • TeX 的處理標記的機制數量非常有限。您看到的程式碼相當於

    eval(r"\pgfpatharc{90}{-90}{" + pgfyc + "}")
    

    在 Python 語法中 --- 程式碼作為標記清單進行操作,然後執行。

    另一種方法是\expandafter在程式碼中的任何地方添加,這肯定效率較低。 (還有\expanded{\unexpanded{...}},但在編寫程式碼時還不存在。)

  • 使用這個習語的其他地方可以看到,例如如何pgfplots在循環中使用繪圖\edef\temp{...}\temp

  • 前綴pgf@表示巨集名稱是 pgf 套件內部的。如果您不編輯 PGF/TikZ 原始碼,則應該使用自己的名稱而不是重複使用\pgf@marshal.

相關內容