怎麼運作的

怎麼運作的

我創建了一個問題之前關於我的程式碼,但它太大且模糊,無法正確隔離我的問題(並且程式碼同時已更改)。這是我關於非常簡單的玩具範例的問題的新版本,使其更加精確且非常簡短。

我嘗試創建一個可以使用相同參數呼叫的函數,例如:

\myFunction{foo} some text \myFunction{foo}

但我需要這個函數在第二種情況下給出不同的結果,此外,僅在第一種情況下定義標籤(以便“\ref”僅引用命令的第一次調用)。

需要的結果:

"Foo is OK and labeled" some text "you have defined foo before, this is not labeled" ! 

我已經嘗試了幾種方法,但並不能可靠地給出這個結果。根據環境或/和多次編譯,結果往往會有所不同。

我嘗試使用標籤機制,因為它整合了一些有用的警告,並且看起來像 \label{foo} 創建一個我寫的變數 r@foo :

\newcommand{\MyTesting}[1]
{
    \ifcsname r@#1\endcsname
        Already defined
    \else
        \label{#1}
    \fi
} 

這樣做的結果是......奇怪的,因為標籤似乎在 aux 檔案(或類似​​的其他檔案)中寫入一個簡單的調用,例如:

\MyTesting{test}

將透過連續的編譯給出:

  1. 標籤可能已更改,重新運行以獲得正確的交叉引用
  2. 沒有什麼
  3. 標籤可能已更改,重新運行以獲得正確的交叉引用
  4. 沒有什麼
  5. ETC...

所以,結果似乎把一個編譯變成了兩個,這不是我們想要的結果。

但目前這並不重要。讓我們測試一下:

\MyTesting{test} some text \MyTesting{test}

透過連續編譯,我們得到:

  1. 標籤可能已更改,重新運行以獲得正確的交叉引用
  2. 標籤“測試”多重定義
  3. 標籤可能已更改,重新運行以獲得正確的交叉引用
  4. 標籤“測試”多重定義
  5. ETC...

這裡我並沒有真正理解邏輯...即使標籤保存在 aux 中, \MyTesting 開頭的測試應該防止多重定義。

答案的獎勵標準:函數的呼叫也應該透過像「圖」中的標題這樣的環境保持穩健,該環境似乎被評估了兩次...

我對這個問題尋求任何幫助;)

MWE:

%%%% work with koma-script, should also work on standard classes %%%%
\documentclass{book}

\usepackage[english]{babel}  

\usepackage{lmodern} 
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{graphicx} % only for testing
\usepackage{floatrow} % for testing
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\makeatletter
\newcommand{\MyLabel}[1]
{
    \ifcsname r@#1\endcsname
        Already defined
    \else
        \label{#1}
    \fi
} 
\makeatother

%%%%%% begin %%%%%%%
\begin{document}
%%%%%% TEST %%%%%%

\chapter{TEST}
\section{Introduction}

Try to label a first time \MyLabel{Firsttest}
Try to label a second time with the same \MyLabel{Firsttest}

%%% for testing in a caption, you can uncomment this part of code %%%

%\begin{figure}[h]
%\centering
%\includegraphics[scale=0.2]{images/Tux.png}
%\caption{A caption}%
%\end{figure}

%%% for testing in a floatrow, you can uncomment this part of code %%%

%\begin{figure}[ht]
%   \centering
%   {
%     \begin{floatrow}[1]
%        \ffigbox[\FBwidth]{\caption{A caption}}{\includegraphics[scale=0.3]{images/Tux.png}}
%     \end{floatrow}
%   }
%\end{figure}

\end{document}

答案1

您的測試\r@label測試標籤是否已定義根據.aux文件。事實上,將呼叫寫入\label檔案。該文件已被讀取:\newlabel.aux.aux

  • 有時\enddocument,這允許 LaTeX 對多重定義的標籤發出警告;

  • 在開始文件時,這允許文件\newlabel中存在的呼叫為先前編譯運行期間定義的每個標籤.aux進行定義。\r@label\label

因此:

  1. 當你的巨集發現\r@test已定義時,這意味著它\label{test}之前的編譯運行;它將列印“已經定義”並且不會打電話 \label{test}在這次運行中,這適用於全部test在此編譯運行期間使用參數呼叫巨集。

  2. 下次編譯時,該.aux檔案將不會有任何\newlabel對 label 的調用test,因此您的巨集將始終發現\r@test未定義,並且始終會\label{test}在此編譯運行中調用,因此每次都會輸出“Label 'test' multiple Defined”警告test在此編譯運行期間。這些\label{test}呼叫將 write\newlabel呼叫寫入test文件.aux,因此在下一次編譯運行時,我們將回到步驟 1。

我相信您想要的是以下內容。該\ifx\protect\@typeset@protect測試使我們能夠確保沒有任何內容洩漏到圖形標題在表格列表或數字列表中(在排版期間該測試是正確的,但當標題通過.lot或文件寫入時則不然— 後者使用,這暫時使- 等於)。.lof\addtocontents\protected@write\protect \let\@unexpandable@protect

編輯:好吧,由於工作原理的原因,在標題內floatrow處理for比這複雜得多,但以下內容似乎工作正常。請注意,需要多次編譯運行才能使標籤穩定。\MyLabelfloatrow

\documentclass{article}
\usepackage{etoolbox}
\usepackage{refcount}
\usepackage{graphicx} % only for testing
\usepackage{floatrow} % only for testing

\makeatletter
\newcommand*{\myInit}[1]{%
  \renewcommand*{\do}[1]{\newcounter{mycount@##1}}%
  \docsvlist{#1}%
  \AtBeginDocument{%
    \renewcommand*{\do}[1]{%
      \ifcsundef{my@goodvalue@##1}{\def\@currentlabel{??}\label{##1}}{}}%
    \docsvlist{#1}%
  }%
}

\newcommand*{\my@MaybeDefine}[2]{%
  \ifcsundef{my@goodvalue@#1}{\csgdef{my@goodvalue@#1}{#2}}{}%
}

\newcommand*{\my@WriteCtr}[2]{%
  \write\@auxout{\string\my@MaybeDefine{#1}{#2}}%
}

\newcommand*{\MyLabel}[2]{%
  \ifx\protect\@typeset@protect
    \stepcounter{mycount@#1}%
    \edef\my@internal@label{my@internal@label@#1@\number\value{mycount@#1}}%
    \ifcsdef{my@goodvalue@#1}
      {\ifnum\value{mycount@#1}=\csuse{my@goodvalue@#1}
        \refstepcounter{#2}%
        \label{#1}%
       \else
         \IfRefUndefinedBabel{#1}{}{% Ref #1 is defined
           \IfRefUndefinedBabel{\my@internal@label}
             {}
             {%
               \ifnum\getpagerefnumber{\my@internal@label}=\getpagerefnumber{#1}
                 the special label is defined earlier on the same page%
               \else
                   \ifnum\getpagerefnumber
                           {\my@internal@label}>\getpagerefnumber{#1}
                     the special label was defined on an earlier page%
                   \fi
               \fi
             }%
         }%
       \fi
      }
      {\typeout{You need to rerun LaTeX for the special labels.}}%
    \label{\my@internal@label}%
    \begingroup
      \edef\tmp{\endgroup\noexpand\my@WriteCtr{#1}{\number\value{mycount@#1}}}%
    \tmp
  \fi
}
\makeatother

\myInit{First-test, Second-test} % The special labels

\newcounter{example}
\setcounter{example}{0}         % not really needed: this is done implicitly

\begin{document}

\listoffigures

\section{Introduction}

Try to label a first time\MyLabel{First-test}{example}.
Try to label a second time with the same: \MyLabel{First-test}{example}.

Label \verb|First-test| is on page~\pageref{First-test} and corresponds to
value~\ref{First-test} of the \verb|example| counter. Label \verb|Second-test|
is on page~\pageref{Second-test} and corresponds to value~\ref{Second-test} of
the \verb|example| counter.

\begin{figure}
  \centering
  \includegraphics[scale=0.2]{example-image-a}
  \caption{A caption.}
\end{figure}

\begin{table}[p]
  \centering
   Some floating material that will appear late in the PDF output:
   \MyLabel{Second-test}{example}.%
   \label{a-table}%
   \caption{A table environment}
\end{table}

\begin{figure}[ht]
  \centering
  \begin{floatrow}[1]
     \ffigbox[\FBwidth]
       {\caption{Another caption\MyLabel{Second-test}{example}}}
       {\includegraphics[scale=0.3]{example-image-b}}
  \end{floatrow}
\end{figure}

Calling \verb|\MyLabel{Second-test}{example}| a third time:
\MyLabel{Second-test}{example}.

\end{document}

在此輸入影像描述

怎麼運作的

請注意,這有點技術性。我們遇到的主要問題floatrow是它多次排版標題文本,\protect等於\@typeset@protect(在我的測試中,單一標題為 5 次!)。事實上,在決定發貨之前,它似乎通過幾種方式進行了衡量。所以,對於每一個特定的特殊標籤(那些聲明\myInit和使用\MyLabel),我們需要檢測它第一次被運送的時間(即發送到 DVI 或 PDF 文件)並且僅限這次使用\label。對於較早的時間,我們必須不輸出任何內容(否則,我們可能會幹擾測量),而對於較晚的時間,我們需要按照問題中的要求輸出“已定義”,但不\label調用。

現在,如何\MyLabel偵測給定標籤何時首次出貨?對於每個標籤,它會計算在排版模式下呼叫它的次數(\protect等於\@typeset@protect),\write並將計數器的對應值寫入.aux檔案(這是valuein \my@MaybeDefine{special label}{value})。這是主要技巧。 A\write是一個那是什麼(請參閱TeXbook),因此可以進入盒子的東西,並且如果包含 Whatsit 的盒子被運出,它只會導致實際寫入文件。因此,用於測量標題文字的虛擬呼叫或其他套件使用的虛擬呼叫floatrow以這種方式處理:不發貨,不寫入檔案.aux。寫入文件的第value一個表示第一次與發貨的盒子內的第一個參數一起使用。因此,當內部計數器等於第一個值時,假設原始檔案自上次編譯以來沒有更改,這意味著包含的材料第一次「真正」排版。\my@MaybeDefine{special label}{value}.aux\MyLabelspecial labelspecial labelspecial label

還有一件事:由於浮動(表格、數字......),某些與某個相關的資料可能特殊標籤排版(即使\protect等於\@typeset@protect)早於\label指令特殊標籤,但稍後出現在輸出檔中。在這種情況下,與相關的內部計數器特殊標籤當為早期浮動材料排版時,其值將低於“良好值”,但它仍然需要“已定義”文本,因為材料將晚於\label.因此,我添加了內部標籤,當內部計數器與「良好價值」不同時,我會將出現內部標籤的頁面(如果確實出現)與找到內部標籤的頁面進行比較\label{special label}。當內容沒有運出時(floatrow進行測量等),相應的內部標籤沒有被定義,因此文本,我修改為“特殊標籤在同一頁上較早定義”和“特殊標籤是在前面的頁面上定義”不會幹擾測量(請參閱代碼)。

是的,這有點hackish!

相關內容