작동 원리

작동 원리

질문을 만들었어요이전에내 코드에 대해 설명했지만 문제를 적절하게 분리하기에는 너무 크고 모호했습니다(그리고 그 사이에 코드가 변경되었습니다). 더 정확하고 매우 짧게 만드는 매우 간단한 장난감 예제에 대한 내 질문의 새 버전은 다음과 같습니다.

다음과 같은 인수로 호출할 수 있는 함수를 만들려고 합니다.

\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. 등...

따라서 결과는 하나의 컴파일을 두 개로 변경하는 것처럼 보이며 이는 원하는 결과가 아닙니다.

그러나 이 시점에서는 이것이 중요하지 않습니다. 다음을 사용하여 테스트해 보겠습니다.

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

연속적인 컴파일을 통해 다음과 같은 결과를 얻을 수 있습니다.

  1. 라벨이 변경되었을 수 있습니다. 상호 참조를 올바르게 하려면 다시 실행하세요.
  2. 라벨 'test' 곱셈이 정의됨
  3. 라벨이 변경되었을 수 있습니다. 상호 참조를 올바르게 하려면 다시 실행하세요.
  4. 라벨 'test' 곱셈이 정의됨
  5. 등...

여기서는 실제로 논리를 이해하지 못합니다... 라벨이 보조에 저장되어 있더라도 \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

  • \enddocumentLaTeX는 다중 정의 레이블에 대해 경고할 수 있습니다 .

  • 문서 시작 시간에 \newlabel파일에 있는 호출이 이전 컴파일 실행 중에 정의된 각 레이블을 .aux정의할 수 있습니다.\r@label\label

따라서:

  1. 매크로가 정의된 것으로 확인되면 해당 매크로 가 다음에서 \r@test호출되었음을 의미합니다.\label{test}이전 컴파일 실행; "이미 정의됨"이 인쇄되고전화하지 않을 거야 \label{test}이번 실행에서는모두test이 컴파일 실행 중에 인수를 사용하여 매크로를 호출합니다 .

  2. 다음에 컴파일할 때 파일에는 label 에 대한 호출 .aux이 없으므로 매크로는 항상 정의되지 않은 항목을 찾아 이 컴파일 실행에서 항상 호출하므로 "Label 'test' Multiply Defined" 경고가 출력됩니다. 이 컴파일 실행 중에 매크로가 인수와 함께 호출되는 시간입니다 . 호출 은 파일 에 대한 호출을 작성 하므로 다음 컴파일 실행 시 1단계로 돌아갑니다.\newlabeltest\r@test\label{test}test\label{test}\newlabeltest.aux

나는 당신이 원하는 것이 다음과 같다고 믿습니다. 테스트 \ifx\protect\@typeset@protect를 통해 그림 캡션에 누출이 없는지 확인할 수 있습니다.표 목록이나 그림 목록에서(이 테스트는 조판 중에는 참이지만 캡션이 를 통해 .lot또는 파일에 기록될 때는 그렇지 않습니다 . 후자는 를 사용하여 일시적으로 -equal을 만듭니다 )..lof\addtocontents\protected@write\protect \let\@unexpandable@protect

편집: 음, 작동 방식으로 인해 캡션 내부를 floatrow처리하는 것은 그보다 훨씬 복잡하지만 다음은 잘 작동하는 것 같습니다. 레이블을 안정화하려면 여러 번의 컴파일 실행이 필요합니다.\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), 따라서 상자 안에 들어가는 것, 그리고내용이 담긴 상자가 배송된 경우에만 실제로 파일에 쓰기가 수행됩니다.. 따라서 floatrow캡션 텍스트를 측정하기 위해 다른 패키지 에서 사용하는 더미 호출 과 이러한 방식으로 처리되지 않는 것은 배송되지 않으며 .aux파일에 쓰지 않습니다. 파일 에 value처음 기록된 것은 배송된 상자 내부의 첫 번째 인수와 함께 처음 사용되었음을 나타냅니다. 따라서 내부 카운터가 이 첫 번째 값과 같을 때 소스 파일이 마지막 컴파일 이후 변경되지 않았다고 가정하면 이는 포함된 자료가 처음으로 "실제"로 조판된다는 의미입니다.\my@MaybeDefine{special label}{value}.aux\MyLabelspecial labelspecial labelspecial label

한 가지 더: 부동 소수점(표, 그림...)으로 인해 일부 자료가특수 라벨에 대한 명령 보다 먼저 조판되어야 합니다( \protect와 동일 하더라도 ).\@typeset@protect\label특수 라벨이지만 나중에 출력 파일에 나타납니다. 이러한 경우, 해당 내부 카운터는특수 라벨자료가 초기 float에 대해 조판될 때 "좋은 값"보다 낮은 값을 가지게 되지만, 여전히 자료가 \label. 이런 이유로 내부 레이블을 추가하고 내부 카운터가 "좋은 값"과 다를 때 내부 레이블이 나타나는 페이지와 내부 레이블이 나타나는 페이지를 비교합니다 \label{special label}. 내용물이 출고되지 않을 때( floatrow측정 등) 해당 내부 라벨이 정의되지 않아 "특수 라벨은 같은 페이지에 미리 정의되어 있습니다." 및 "특수 라벨은 다음과 같습니다"로 수정했습니다. 이전 페이지에 정의됨”은 측정을 방해하지 않습니다(코드 참조).

예, 이건 약간 해킹적입니다!

관련 정보