2

Very often I want to refer to a certain claim I mentioned earlier in the document: "As I said earlier, blah." Since I often rearrange material, I need to ensure that "blah" indeed occurs earlier (it doesn't help to know that it occurs somewhere in the document). So I mark it with \label{blah}.

I am using the macro \aboveorbelow defined here. It doesn't quite do the trick: if I write, "As I said \aboveorbelow{blah}, blah", then the final draft may have the sentence "As I said below, blah", which is wrong, and I won't be aware of it. So I want to take this one step further. I want to have a conditional macro roughly of this form:

\def\earlier#1{
 \ifthenelse{\EQUAL{\aboveorbelow{#1}}{above}}
   {% True case
    \relax%
   }
   {% false case
    ERROR% preferably throw an error
   }
}

Then I could say, "As I said\earlier{blah} earlier, blah." I tried to use \IfStrEq from xstring, but no luck. Is it possible to do? I believe there's no package doing that, which is strange, since this is a recurrent problem of long manuscripts.

10
  • 1
    Maybe the varioref package could be useful Commented Jun 18 at 8:08
  • 1
    Please make your code compilable (if possible), or at least complete it with \documentclass{...}, the required \usepackage's, \begin{document}, and \end{document}. That may seem tedious to you, but think of the extra work it represents for TeX.SX users willing to give you a hand. Help them help you: remove that one hurdle between you and a solution to your problem.
    – cabohah
    Commented Jun 18 at 8:28
  • 2
    Why don’t you just use this package ctan.org/pkg/varioref, which would also print the page referred to!
    – MS-SPO
    Commented Jun 18 at 8:42
  • 1
    if varioref solves this problem, I'd love to know how.
    – Jabot
    Commented Jun 18 at 8:51
  • 1
    @Jabot Don't stop reading comments at the first comma. Also read the rest starting with "or at least …". And if you get error, also always copy&paste the error message from the log file into the question.
    – cabohah
    Commented Jun 18 at 9:06

2 Answers 2

3

As the ifthen package documentation explains, \equal can only compare strings:

If these expansions are equal, then the proposition is true.

But the \aboveorbelow{…} of David's answer to the linked question does never expand to a string. If the definition of a command contains \def, \edef, \xdef, \write or similar, they are even not expandable. So using \aboveorbelow cannot be used in the comparison of \ifthenelse.

But to see, if something has been used before you don't need the indirect solution using the aux file and labels. Instead you can use a marker macro:

\documentclass{article}

\usepackage{mwe}

\makeatletter
\newcommand*{\say}[1]{\@namedef{said@#1}{}}
\newcommand*{\earlier}[1]{%
  \@ifundefined{said@#1}{%
    \@latex@error{#1 not yet said}{}%
  }{}%
}
\makeatother

\begin{document}

\say{einstein}Einstein was one of the greates physicists of the
  20th century. \Blindtext

As said\earlier{einstein} earlier, blah.

As also said\earlier{cabohah}, foo.% This results intentionally in an error! To see a error free result either do \say{cabohah} before or comment the line.
  
\end{document}

But instead of reporting an error, you could alternatively do something like:

\documentclass{article}

\usepackage{mwe}

\makeatletter
\NewDocumentCommand{\SayIt}{O{As already said on page~\pageref{say:#2}, }mm}{%
  \@ifundefined{said@#2}{%
    \@namedef{said@#2}{}%
    \csname phantomsection\endcsname\label{say:#2}#3%
  }{%
    #1#3%
  }%
}
\makeatother

\begin{document}

\SayIt{einstein}{Einstein was one of the greates physicists of the
  20th century}. \Blindtext[4]

\SayIt{einstein}{Einstein was one of the greates physicists of the
  20th century}. \Blindtext

\SayIt[But as already said on \pageref{say:einstein}: ]{einstein}{Einstein was one of the greates physicists of the
  20th century}.

\end{document}

But then you could also use a kind of glossary or acronym solution using, e.g., package glossaries or package acro (or one of the packages listed with the same topics on the linked CTAN entries).

The first example can be adapted in many ways. For example, instead of saying the same again an again, the marker macro can store the already said and be used to repeat it. And instead of an error, you can use a warning and output a marker similar to undefined references:

\documentclass{article}

\usepackage{mwe}

\makeatletter
\newcommand{\say}[2]{\@namedef{said@#1}{#2}#2}
\NewDocumentCommand{\earlier}{mO{\@nameuse{said@#1}}}{%
  \@ifundefined{said@#1}{%
    \@latex@warning{#1 not yet said}%
    \textbf{??}%
  }{#2}%
}
\makeatother

\begin{document}

\say{einstein}{Einstein was one of the greates physicists of the
  20th century}. \blindtext

As said earlier, \earlier{einstein}.

As said earlier, \earlier{einstein}[Einstein was genius!]% use the optional argument to say it different

As also said earlier, \earlier{cabohah}.% warning and marker output
  
\end{document}

enter image description here

Note: If you want to use a marker macro for forward references you would need to write them to the auxiliary file. But in such a case, using a label and test for the existence of the label would IMHO make more sense. But this wasn't asked in the question, so I don't show it.

And because you are insisting in using \label though I think that it does not make sense in your case. You can do it be changing \aboveorbelow to either already handle your error case or delegate the two cases to other commands you can easily change:

\documentclass{article}

\newcounter{zzz}

\makeatletter
\def\aboveorbelow#1{%
\stepcounter{zzz}%
\edef\zztmp{\xdef\string\zzz\Roman{zzz}}%
\write\@auxout\expandafter{\zztmp{\string\@ifundefined{r@#1}{\cmdbelow{#1}}{\cmdabove{#1}}}}%
\csname zzz\Roman{zzz}\endcsname}
\NewDocumentCommand\cmdbelow{m}{\@latex@error{#1 unknown or below}{}}
\NewDocumentCommand\cmdabove{m}{\@gobble{#1 set before}}
\makeatother

\usepackage{mwe}

\begin{document}

\section{Einstein}
\label{say:einstein}Einstein was one of the greates physicists of the
  20th century. \blindtext

As said\aboveorbelow{say:einstein} earlier, blah.

As also said\aboveorbelow{say:cabohah}, foo.% This results intentionally in an error! To see a error free result either do \label{say:cabohah} before or comment the line.

As also said\aboveorbelow{say:jabot}, Jabot want to use \verb|\label|.% This results intentionally in an error! To see a error free result either move it after \label{say:cabohah} or comment the line.

\section{Jabot}

\label{say:jabot}Jabot wants to use \verb|\label|.

\end{document}

But note, that this solution needs at least two LaTeX runs for a valid result. And if you move a \label or \aboveorbelow or add another \aboveorbelow again the next LaTeX run does not always show the correct result. And because incorrect result can also mean incorrect error message and can stop automatic build processes, this can be very annoying. So I would prefer the examples without using \label.

5
  • 1
    The first solution doesn't work, I think: the text is gobbled. The second solution is about something else altogether. Generally, I already have 90% of the solution with \aboveorbelow, I just need to compare the strings...
    – Jabot
    Commented Jun 18 at 8:44
  • After commenting out the last line, the first solution still throws an error LaTeX Error: einstein not yet said. which is not the expected result.
    – Jabot
    Commented Jun 18 at 9:30
  • Please try it again. I was still editing the answer to add the explanation, that you cannot use \aboveorbelow. And because you are still not showing a complete example in your question, guessing around, what you are doing is somehow time consuming.
    – cabohah
    Commented Jun 18 at 9:36
  • Great, the first solution now works, I also added a test to check if the marker is defined: ``` \newcommand*{\say}[1]{\@ifundefined{said@#1}{% \@namedef{said@#1}{}}{\@latex@error{#1 already defined}{}}} ``` I'm still wondering if there's a flexible solution that works with \label, since this command can be reused for other purposes like section references.
    – Jabot
    Commented Jun 18 at 9:59
  • @Jabot I see no benefit in using \label for this purpose. But because you insist in using it …
    – cabohah
    Commented Jun 18 at 11:50
4

As long as you can formalize your claims as numbered theorems, equations or something similar, the varioref package and its \vref user macro are your friends.

The example code given below generates the following two cross-references:

enter image description here

enter image description here

Observe that the strings "on page 5" and "on the previous page" are generated dynamically. If you move the material associated with thm:einstein and eq:pyth to other parts of the document, these strings will be updated upon recompilation.

\documentclass{article}

\usepackage{amsmath,amsthm}
\usepackage{varioref} % for '\vref' macro
\usepackage{lipsum}   % filler text
\usepackage[colorlinks,allcolors=blue]{hyperref} % optional
\usepackage[nameinlink,noabbrev]{cleveref}       % optional
\newtheorem{theorem}{Theorem}

\begin{document}

As stated in \vref{thm:einstein}, \dots

\begin{equation} \label{eq:pyth} a^2+b^2=c^2 \end{equation}

\clearpage

As illustrated in \vref{eq:pyth}, \dots

\bigskip

\lipsum[1-20] % several pages of filler text

\begin{theorem} \label{thm:einstein} $E=mc^2$ \end{theorem}

\end{document}
2
  • trouble is, I don't use page references at all, since many journals don't like them (because scrambled in published versions)... Plus, in my case it is just plain text outside any environment.
    – Jabot
    Commented Jun 18 at 10:02
  • @Jabot - Any journal of decent repute will likely discourage you from inserting vague claims such as "As I said earlier, blah...". If a journal allows cross-references, these better be unambiguous.
    – Mico
    Commented Jun 18 at 12:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .