
질문을 만들었어요이전에내 코드에 대해 설명했지만 문제를 적절하게 분리하기에는 너무 크고 모호했습니다(그리고 그 사이에 코드가 변경되었습니다). 더 정확하고 매우 짧게 만드는 매우 간단한 장난감 예제에 대한 내 질문의 새 버전은 다음과 같습니다.
다음과 같은 인수로 호출할 수 있는 함수를 만들려고 합니다.
\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}
연속적인 편집을 통해 제공됩니다.
- 라벨이 변경되었을 수 있습니다. 상호 참조를 올바르게 하려면 다시 실행하세요.
- 아무것도 아님
- 라벨이 변경되었을 수 있습니다. 상호 참조를 올바르게 하려면 다시 실행하세요.
- 아무것도 아님
- 등...
따라서 결과는 하나의 컴파일을 두 개로 변경하는 것처럼 보이며 이는 원하는 결과가 아닙니다.
그러나 이 시점에서는 이것이 중요하지 않습니다. 다음을 사용하여 테스트해 보겠습니다.
\MyTesting{test} some text \MyTesting{test}
연속적인 컴파일을 통해 다음과 같은 결과를 얻을 수 있습니다.
- 라벨이 변경되었을 수 있습니다. 상호 참조를 올바르게 하려면 다시 실행하세요.
- 라벨 'test' 곱셈이 정의됨
- 라벨이 변경되었을 수 있습니다. 상호 참조를 올바르게 하려면 다시 실행하세요.
- 라벨 'test' 곱셈이 정의됨
- 등...
여기서는 실제로 논리를 이해하지 못합니다... 라벨이 보조에 저장되어 있더라도 \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
따라서:
매크로가 정의된 것으로 확인되면 해당 매크로 가 다음에서
\r@test
호출되었음을 의미합니다.\label{test}
이전 컴파일 실행; "이미 정의됨"이 인쇄되고전화하지 않을 거야\label{test}
이번 실행에서는모두test
이 컴파일 실행 중에 인수를 사용하여 매크로를 호출합니다 .다음에 컴파일할 때 파일에는 label 에 대한 호출
.aux
이 없으므로 매크로는 항상 정의되지 않은 항목을 찾아 이 컴파일 실행에서 항상 호출하므로 "Label 'test' Multiply Defined" 경고가 출력됩니다. 이 컴파일 실행 중에 매크로가 인수와 함께 호출되는 시간입니다 . 호출 은 파일 에 대한 호출을 작성 하므로 다음 컴파일 실행 시 1단계로 돌아갑니다.\newlabel
test
\r@test
\label{test}
test
\label{test}
\newlabel
test
.aux
나는 당신이 원하는 것이 다음과 같다고 믿습니다. 테스트 \ifx\protect\@typeset@protect
를 통해 그림 캡션에 누출이 없는지 확인할 수 있습니다.표 목록이나 그림 목록에서(이 테스트는 조판 중에는 참이지만 캡션이 를 통해 .lot
또는 파일에 기록될 때는 그렇지 않습니다 . 후자는 를 사용하여 일시적으로 -equal을 만듭니다 )..lof
\addtocontents
\protected@write
\protect
\let
\@unexpandable@protect
편집: 음, 작동 방식으로 인해 캡션 내부를 floatrow
처리하는 것은 그보다 훨씬 복잡하지만 다음은 잘 작동하는 것 같습니다. 레이블을 안정화하려면 여러 번의 컴파일 실행이 필요합니다.\MyLabel
floatrow
\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
(이것은 value
in \my@MaybeDefine{special label}{value}
)입니다. 이것이 주요 트릭입니다. A \write
는이게 뭐야(참조, TeXbook), 따라서 상자 안에 들어가는 것, 그리고내용이 담긴 상자가 배송된 경우에만 실제로 파일에 쓰기가 수행됩니다.. 따라서 floatrow
캡션 텍스트를 측정하기 위해 다른 패키지 에서 사용하는 더미 호출 과 이러한 방식으로 처리되지 않는 것은 배송되지 않으며 .aux
파일에 쓰지 않습니다. 파일 에 value
처음 기록된 것은 배송된 상자 내부의 첫 번째 인수와 함께 처음 사용되었음을 나타냅니다. 따라서 내부 카운터가 이 첫 번째 값과 같을 때 소스 파일이 마지막 컴파일 이후 변경되지 않았다고 가정하면 이는 포함된 자료가 처음으로 "실제"로 조판된다는 의미입니다.\my@MaybeDefine{special label}{value}
.aux
\MyLabel
special label
special label
special label
한 가지 더: 부동 소수점(표, 그림...)으로 인해 일부 자료가특수 라벨에 대한 명령 보다 먼저 조판되어야 합니다( \protect
와 동일 하더라도 ).\@typeset@protect
\label
특수 라벨이지만 나중에 출력 파일에 나타납니다. 이러한 경우, 해당 내부 카운터는특수 라벨자료가 초기 float에 대해 조판될 때 "좋은 값"보다 낮은 값을 가지게 되지만, 여전히 자료가 \label
. 이런 이유로 내부 레이블을 추가하고 내부 카운터가 "좋은 값"과 다를 때 내부 레이블이 나타나는 페이지와 내부 레이블이 나타나는 페이지를 비교합니다 \label{special label}
. 내용물이 출고되지 않을 때( floatrow
측정 등) 해당 내부 라벨이 정의되지 않아 "특수 라벨은 같은 페이지에 미리 정의되어 있습니다." 및 "특수 라벨은 다음과 같습니다"로 수정했습니다. 이전 페이지에 정의됨”은 측정을 방해하지 않습니다(코드 참조).
예, 이건 약간 해킹적입니다!