Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

This question follows on from this one, which I asked a few weeks ago. I have since discovered the cleveref package, and I would like to use it for ease in referring to theorem-like environments without having to remember whether a given statement was a theorem, a lemma, a proposition, etc.

Here is a small example document. When I compile this document, the final line of text begins "Here are some references to ?? 1.1, ?? 1.2 and theorem 1.3." The attempt to use \crefname to set reference names (I'm not up on the terminology) for the different environments is not working. However, if the line marked with % (*) is uncommented, the second "??" will become "toybox". So it is something to do with being in a \foreach. But I can't find a way to make it work. It is as though the invocation of \crefname is totally ignored within the \foreach. Why does this happen?

\documentclass[a4paper]{amsart}

\def\myTheoremEnvironments{%
    theorem/theorems,%
    cobblestone/cobblestones,%
    toybox/toyboxes%
}

\usepackage{tikz, titlecaps, cleveref}

% Number all definition, theorem, etc. environments using the same counter.
% Start counting again at the start of each section.
% Style these environments using LaTeX's "definition" style.
\theoremstyle{definition}
\newtheorem{baseTheorem}{Base Theorem}[section]
\foreach \x/\y in \myTheoremEnvironments {
    \edef\tmp{\noexpand\newtheorem{\x}[baseTheorem]{\noexpand\titlecap{\x}}}\tmp
    \crefname{\x}{\x}{\y}
}

%\crefname{toybox}{toybox}{toyboxes} % (*)

\begin{document}

\section{Hello, and welcome to my document.}

\begin{cobblestone}
    \label{csref}
    Hello. I am an ``cobblestone'' environment.
\end{cobblestone}

\begin{toybox}
    \label{tbref}
    I am a ``toybox'' environment. What a strange name that is for an environment.
\end{toybox}

\begin{theorem}
    \label{thmref}
    People trying to do strange things with ``foreach'' should expect trouble.
\end{theorem}

Here are some references to \cref{csref}, \cref{tbref} and \cref{thmref}.
I could have tried using just one call to ``cref'' here,
but it's best not to run before you can walk.

\end{document}
share|improve this question
    
The main problem is the usual one: \foreach executes its cycles in a group and \crefname only does local assignments. –  egreg Nov 16 '13 at 15:10
    
The author of the cleveref package, @TobyCubitt, appears now to be active on this StackExchange. –  Niel de Beaudrap Nov 16 '13 at 20:39
1  
I couldn't remember if there was a good reason for making \crefname do local assignments. Luckily, my former and smarter self kindly left an explanation in the manual (implementation section). To quote myself: "\crefname must not create global definitions, or else it will break babel's \otherlanguage and \foreignlanguage commands." I no longer recall why this will break babel, but presumably the answer's somewhere in the babel manual/code. –  Toby Cubitt Nov 18 '13 at 21:44

3 Answers 3

up vote 5 down vote accepted

The big problem is that \foreach does each cycle inside a group; while \newtheorem definitions are global, \crefname definitions aren't.

I suggest a perhaps more user-friendly interface.

\documentclass[a4paper]{amsart}

\usepackage{titlecaps, cleveref}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\foreachpair}{smm}
 {
  \IfBooleanTF{#1}
   {
    \hammerite_foreachpair:on { #2 } { #3 }
   }
   {
    \hammerite_foreachpair:nn { #2 } { #3 }
   }
 }

\cs_new_protected:Npn \hammerite_foreachpair:nn #1 #2
 {
  \cs_set:Npn \__hammerite_temp:nn ##1 ##2 { #2 }
  \clist_map_inline:nn { #1 } { \__hammerite_dopair:n { ##1 } }
 }
\cs_new:Npn \__hammerite_dopair:n #1
 {
  \__hammerite_dopair:wn #1 \q_stop
 }
\cs_new:Npn \__hammerite_dopair:wn #1/#2 \q_stop
 {
  \__hammerite_temp:nn { #1 } { #2 }
 }
\cs_generate_variant:Nn \hammerite_foreachpair:nn { o }
\ExplSyntaxOff

% Number all definition, theorem, etc. environments using the same counter.
% Start counting again at the start of each section.
% Style these environments using LaTeX's "definition" style.

\theoremstyle{definition}

\newtheorem{baseTheorem}{Base Theorem}[section]

\foreachpair
 {
  theorem/theorems,
  cobblestone/cobblestones,
  toybox/toyboxes,
 }
 {
  \newtheorem{#1}[baseTheorem]{\titlecap{#1}}
  \crefname{#1}{#1}{#2}
 }

\begin{document}

\section{Hello, and welcome to my document.}

\begin{cobblestone}\label{csref}
Hello. I am an ``cobblestone'' environment.
\end{cobblestone}

\begin{toybox}\label{tbref}
I am a ``toybox'' environment. What a strange name that is for an environment.
\end{toybox}

\begin{theorem}\label{thmref}
People trying to do strange things with ``foreach'' should expect trouble.
\end{theorem}

Here are some references to \cref{csref}, \cref{tbref} and \cref{thmref}.
I could have tried using just one call to ``cref'' here,
but it's best not to run before you can walk.

\end{document}

As you see, the second argument of \foreachpair is a comma separated list of items of the form <item-a>/<item-b>; the second argument is like a command definition, at each cycle <item-a> is substituted for #1 and <item-b> is substituted for #2.

There's also an alternative way of calling it

\newcommand\myTheoremEnvironments{
  theorem/theorems,
  cobblestone/cobblestones,
  toybox/toyboxes
}

\foreachpair* \myTheoremEnvironments
 {
  \newtheorem{#1}[baseTheorem]{\titlecap{#1}}
  \crefname{#1}{#1}{#2}
 }

enter image description here

share|improve this answer

\foreach does each iteration in a group and furthermore \crefname does not expand its second argument.

One can circumvent these problems using \xintForpair from package xinttools:

\documentclass[a4paper]{amsart}


\usepackage{tikz, titlecaps, cleveref}

% Number all definition, theorem, etc. environments using the same counter.
% Start counting again at the start of each section.
% Style these environments using LaTeX's "definition" style.
\theoremstyle{definition}
\newtheorem{baseTheorem}{Base Theorem}[section]

% Does not work: 
% \def\myTheoremEnvironments{%
%     theorem/theorems,%
%     cobblestone/cobblestones,%
%     toybox/toyboxes%
% }
% \foreach \x/\y in \myTheoremEnvironments {
%     \edef\tmp{\noexpand\newtheorem{\x}[baseTheorem]{\noexpand\titlecap{\x}}}\tmp
%     \crefname{\x}{\x}{\y}
% }

% alternative using \xintForpair:

\usepackage{xinttools}% http://ctan.org/pkg/xint

\xintForpair #1#2 in 
{% spaces around commas or like the end of lines here, 
 % or around parentheses are removed automatically
   (theorem,theorems),
   (cobblestone,cobblestones),
   (toybox,toyboxes)
}
\do
{%  (spaces significant but do not matter)
    \newtheorem{#1}[baseTheorem]{\titlecap{#1}}
    \crefname{#1}{#1}{#2}
}

\begin{document}\thispagestyle{empty}

\section{Hello, and welcome to my document.}

\begin{cobblestone}
    \label{csref}
    Hello. I am an ``cobblestone'' environment.
\end{cobblestone}

\begin{toybox}
    \label{tbref}
    I am a ``toybox'' environment. What a strange name that is for an environment.
\end{toybox}

\begin{theorem}
    \label{thmref}
    People trying to do strange things with ``foreach'' should expect trouble.
\end{theorem}

Here are some references to \cref{csref}, \cref{tbref} and \cref{thmref}.
I could have tried using just one call to ``cref'' here,
but it's best not to run before you can walk.

\end{document}

loops with crefname

share|improve this answer
    
I appreciate the suggestion of the xint loop and I considered using this. It seems like this functionality has only very recently (like, in the last month) been added to xint. The alternative won out in part because I don't have to update my LaTeX installation! –  Hammerite Nov 16 '13 at 21:07
    
@Hammerite yes it is a recent addition. I completely understand your reasons for going with egreg's answer.B –  jfbu Nov 16 '13 at 22:04

[Y]ou can […] use the .list handler that uses \foreach internally but

  • doesn’t group its content […] and
  • can be used with the already expanded content as #1 (rather than as a macro sequence).

Using

\pgfkeys{/utils/my Foreach/.code args={#1/#2}{\newtheorem{#1}[baseTheorem]{\titlecap{#1}}%
                                              \crefname{#1}{#1}{#2}},
         /utils/my Foreach/.list/.expand once=\myTheoremEnvironments}

gives

enter image description here

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.