Cómo funciona

Cómo funciona

he creado una preguntapreviamentesobre mi código, pero era demasiado grande y confuso para aislar adecuadamente mi problema (y el código ha cambiado mientras tanto). Aquí hay una nueva versión de mi pregunta sobre un ejemplo de juguetes muy simples que la hace más precisa y muy breve.

Intento crear una función que pueda llamarse con el mismo argumento como:

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

pero necesito que esta función dé un resultado diferente en el segundo caso y, además, que se defina una etiqueta solo en el primer caso (para que un "\ref" haga referencia solo a la primera llamada del comando).

El resultado necesario:

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

He probado varias cosas que no dan este resultado de manera sólida. El resultado tiende a ser diferente según los entornos y/o las múltiples compilaciones.

Intenté usar el mecanismo de etiquetas porque tiene algunas advertencias integradas que son útiles y como parece que \label{foo} crea una variable r@foo, escribí:

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

El resultado de esto es... extraño, ya que parece que la etiqueta escribe en el archivo auxiliar (u otro archivo como este) una llamada simple como:

\MyTesting{test}

dará a través de las sucesivas recopilaciones:

  1. Es posible que la etiqueta haya cambiado; vuelva a ejecutarla para obtener la referencia cruzada correcta
  2. nada
  3. Es posible que la etiqueta haya cambiado; vuelva a ejecutarla para obtener la referencia cruzada correcta
  4. nada
  5. etc...

Entonces, el resultado parece cambiar una compilación en dos, lo cual no es el resultado deseado.

Pero aún así, en este punto no es crítico. Probémoslo con:

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

Aquí tenemos, a través de la sucesiva recopilación:

  1. Es posible que la etiqueta haya cambiado; vuelva a ejecutarla para obtener la referencia cruzada correcta
  2. Etiqueta 'prueba' definida multiplicadamente
  3. Es posible que la etiqueta haya cambiado; vuelva a ejecutarla para obtener la referencia cruzada correcta
  4. Etiqueta 'prueba' definida multiplicadamente
  5. etc...

Aquí realmente no entiendo la lógica... incluso si la etiqueta se guarda en el auxiliar, la prueba al comienzo de \MyTesting debería evitar la definición múltiple.

Criterios de bonificación para las respuestas: la llamada de la función también debe ser sólida a través de un entorno como el título en la «figura» que parece evaluarse dos veces...

Acepto cualquier ayuda con este problema;)

El 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}

Respuesta1

Su prueba de \r@labelpruebas si la etiqueta ya está definidasegún el .auxexpediente. De hecho, \labelescribe \newlabelllamadas al .auxarchivo. Este .auxarchivo se lee:

  • at \enddocumenttime, lo que permite a LaTeX advertir sobre etiquetas definidas múltiples veces;

  • al inicio del documento, lo que permite que las \newlabelllamadas presentes en el .auxarchivo se definan \r@labelpara cada etiqueta que se definió \labeldurante la ejecución de compilación anterior.

De este modo:

  1. Cuando su macro se encuentra \r@testdefinida, significa que fue llamada \label{test}en elejecución de compilación anterior; imprimirá “ya definido” yno llamará \label{test}en esta carrera, y esto es válido paratodoinvocaciones de su macro con argumento testdurante esta ejecución de compilación.

  2. La próxima vez que compile, el .auxarchivo no tendrá ninguna \newlabelllamada para label test, por lo tanto su macro siempre encontrará que \r@testno está definido y siempre llamará \label{test}en esta ejecución de compilación, de ahí la advertencia "Etiqueta 'prueba' definida múltiplesmente" cada vez. momento en que se llama a su macro con un argumento testdurante esta ejecución de compilación. Las \label{test}llamadas escriben \newlabelllamadas testen el .auxarchivo, por lo tanto, en la próxima ejecución de compilación, volveremos al paso 1.

Creo que lo que quieres es lo siguiente. La \ifx\protect\@typeset@protectprueba nos permite asegurarnos de que no se filtre nada en los subtítulos de las figuras.en la lista de tablas o lista de figuras(La prueba es verdadera durante la composición tipográfica, pero no cuando los subtítulos se escriben en los archivos .loto .lofmediante \addtocontents—este último usa \protected@write, lo que temporalmente hace \protect \let-igual a \@unexpandable@protect).

Editar: bueno, debido a cómo floatrowfunciona, lidiar con \MyLabelel interior de un título floatrowes mucho más complicado que eso, pero lo siguiente parece funcionar bien. Tenga en cuenta que se necesitan varias ejecuciones de compilación para que las etiquetas se estabilicen.

\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}

ingrese la descripción de la imagen aquí

Cómo funciona

Cuidado, esto es un poco técnico. El principal problema que tuvimos floatrowes que escribe el texto del título muchas veces con \protectun valor igual a \@typeset@protect(¡5 veces para un solo título en mi prueba!). De hecho, parece medirlo de varias maneras antes de decidir enviarlo. Entonces, para cada particularetiqueta especial(aquellos declarados \myInity utilizados en \MyLabel), debemos detectar la primera vez a dónde se envía (es decir, se envía al archivo DVI o PDF) ysolo por esta vezusar \label. Para momentos anteriores, no debemos generar nada (de lo contrario, podríamos alterar las mediciones) y para momentos posteriores, debemos generar "ya definido" como se solicita en la pregunta, pero sin \labelllamada.

Ahora bien, ¿cómo se \MyLabeldetecta cuándo se envía una etiqueta determinada por primera vez? Para cada etiqueta, cuenta el número de veces que se llama en modo de composición tipográfica ( \protectigual a \@typeset@protect) y \writeenvía el valor correspondiente del contador al .auxarchivo (este es el valuein \my@MaybeDefine{special label}{value}). Este es el truco principal. una \writees unaque es eso(cf. TeXbook), por lo tanto, algo que se mete dentro de las cajas, ysolo da como resultado una escritura real en un archivo si se envía la caja que contiene el whatsit. Entonces, las llamadas ficticias utilizadas por floatrowu otros paquetes para medir el texto del título y demás se manejan de esta manera: sin envío, sin escritura en el .auxarchivo. El valueprimer \my@MaybeDefine{special label}{value}escrito en el .auxarchivo indica que \MyLabelse usó por primera vez con el primer argumento special labeldentro de una caja que se envió. Entonces, cuando el contador interno for special labeles igual a este primer valor, asumiendo que el archivo fuente no cambió desde la última compilación, esto significa que el material que contiene special labelse está tipografiando “de verdad” por primera vez.

Una cosa más: debido a los flotadores (tablas, figuras...), es posible que algún material asociado a unetiqueta especialescribirse (incluso con \protectigual a \@typeset@protect) antes que el \labelcomando para eletiqueta especial, pero aparecen más adelante en el archivo de salida. En tales casos, el contador interno asociado aletiqueta especialtendría un valor inferior al "buen valor" cuando el material se está componiendo para la publicación inicial, pero aún así, necesita el texto "ya definido" ya que el material aparecerá más tarde que el \label. Por esta razón, agregué etiquetas internas y cuando el contador interno es diferente del "buen valor", comparo la página en la que aparece la etiqueta interna, si es que aparece, con la página donde se \label{special label}encuentra. Cuando el contenido no se envía ( floatrowhaciendo mediciones, etc.), las etiquetas internas correspondientes no se definen, de ahí el texto, que modifiqué a "la etiqueta especial se definió anteriormente en la misma página" y "la etiqueta especial fue definida anteriormente en la misma página". definido en una página anterior” no altera las mediciones (ver código).

¡Sí, esto es un poco hack!

información relacionada