3

I've defined a macro \mychapter having an argument which is a list of two comma separated arguments (Author and Title).

The macro creates a \chapter and assigns the author and the title to a fancy header.

But when I extract the Author (and the Title) and assign them respectively to a variable which I pass to a fancy header. The variable seems not to be properly expanded.

I extract Author and Title via \readlist and later assign them to \z@author and \z@title via \def. Is this the right way?

But the header of the last page of every previous chapter shows the author and title of the next chapter when using \z@author or \z@title.

It works as expected if I use the whole argument #1. But it contains both author and title unseparated.

See image below.

Note: I use a comma separated argument because the actual macro as more than 9 arguments. The example here has been simplified to illustrate the problem.

\documentclass{book}

\usepackage{listofitems}
\usepackage{lipsum}
\usepackage{fancyhdr}

\makeatletter
\def\@makechapterhead#1{}
\newcommand{\mychapter}[1]{%
    \setsepchar{,}
    \readlist\AuthorAndTitle{#1}
    \def\z@author{\AuthorAndTitle[1]}
    \def\z@title{\AuthorAndTitle[2]}
    \chapter{\z@title}
    \pagestyle{fancy}
    \fancyhf{}
    \fancyhead{}
    \fancyhead[LE]{{#1 -- \z@author}}
    \fancyhead[RO]{{#1 -- \z@title}}
}
\makeatother

\begin{document}

\mychapter{{Author 1},{Title 1}}
\lipsum[1-10]
\mychapter{{Author 2},{Title 2}}
\lipsum[2-10]

\end{document}

enter image description here

11
  • 1
    Quite surely an expansion issue, try with \edef\x{\noexpand\fancyhead[LE]{{#1 -- \z@author}}}\x (can't test now).
    – campa
    Oct 20, 2023 at 17:27
  • 4
    Another solution could be using clist functions of latex3 code which is really straightforward.
    – lukeflo
    Oct 20, 2023 at 17:31
  • 1
    @lukeflo "Straightforward" is definitely not one of the the top ten adjectives which come to my mind when thinking of latex3 😅
    – campa
    Oct 20, 2023 at 20:10
  • @lukeflo: can give some link where to start with clist? Web search is not helpful on that. In which package is clist documented?
    – wolfrevo
    Oct 20, 2023 at 20:15
  • @campa: Thanks. This works. But can you explain how it works? And why \noexpand\fancyhead[LE]{{#1 -- \z@author}} or \fancyhead[LE]{{#1 -- \z@author}} is not working?
    – wolfrevo
    Oct 20, 2023 at 20:27

3 Answers 3

2

Page headers are so-called moving arguments which implies that they can wind up in moments when the meaning of underlying macros has already changed or, e.g., if they contain material which also goes to the tables of contents at the begin of the document or goes to the bookmarks of the pdf-file, at the time of processing the moving arguments is not yet assigned/defined as desired.

Therefore a bit of fine-grained expansion control might be nice.

Seems macros defined via listofitems' \readlist, e.g., \AuthorAndTitle, require two expansion-steps to deliver the result, thus you can, e.g., do s.th. like

\documentclass{book}

\usepackage{listofitems}
\usepackage{lipsum}
\usepackage{fancyhdr}

\makeatletter
%%----------------------------------------------------------------------
%% \romannumeral\Expandtimes{<number K>}<tokens>
%%  ->   <tokens> will be hit by \expandafter K times.
%%----------------------------------------------------------------------
\@ifdefinable\UD@innerdfork{\def\UD@innerdfork#1d#2#3dd{#2}}%
\newcommand*\UD@dfork[1]{\UD@innerdfork#1{\@firstoftwo}d{\@secondoftwo}dd}%
\newcommand*\Expandtimes[1]{%
  0\expandafter\UD@innerExp\expandafter{\expandafter}%
  \romannumeral\number\number#1 000d%
}%
\newcommand*\UD@innerExp[2]{\UD@dfork{#2}{#1 }{\UD@innerExp{#1#1\expandafter}}}%
%%----------------------------------------------------------------------
\def\@makechapterhead#1{}
\newcommand{\mychapter}[1]{%
    \setsepchar{,}%
    \readlist\AuthorAndTitle{#1}%
%    \def\z@author{\AuthorAndTitle[1]}%
%    \def\z@title{\AuthorAndTitle[2]}%
%    \expandafter\chapter\expandafter{\romannumeral\Expandtimes{3}\z@title}%
    \expandafter\chapter\expandafter{\romannumeral\Expandtimes{2}\AuthorAndTitle[2]}%
    \pagestyle{fancy}%
    \fancyhf{}%
    \fancyhead{}%
%    \expanded{\unexpanded{\fancyhead[LE]}{{\unexpanded{#1 -- }\unexpanded\expandafter{\romannumeral\Expandtimes{3}\z@author}}}}%
%    \expanded{\unexpanded{\fancyhead[RO]}{{\unexpanded{#1 -- }\unexpanded\expandafter{\romannumeral\Expandtimes{3}\z@title}}}}%
    \expanded{\unexpanded{\fancyhead[LE]}{{\unexpanded{#1 -- }\unexpanded\expandafter{\romannumeral\Expandtimes{2}\AuthorAndTitle[1]}}}}%
    \expanded{\unexpanded{\fancyhead[RO]}{{\unexpanded{#1 -- }\unexpanded\expandafter{\romannumeral\Expandtimes{2}\AuthorAndTitle[2]}}}}%

}
\makeatother

\begin{document}

\mychapter{{Author 1},{Title 1}}

\lipsum[1-10]
\mychapter{{Author 2},{Title 2}}
\lipsum[2-10]

\end{document}

As you asked for it in the comments, let's have a closer look at

\expanded{\unexpanded{\fancyhead[RO]}{{\unexpanded{#1 -- }\unexpanded\expandafter{\romannumeral\Expandtimes{2}\AuthorAndTitle[2]}}}}

First let's do some indenting:

\expanded{%
   \unexpanded{%
      \fancyhead[RO]%
   }%
   {{%
      \unexpanded{#1 -- }%
      \unexpanded\expandafter{%
         \romannumeral\Expandtimes{2}\AuthorAndTitle[2]%
      }%
   }}%
}%

With an expression \expanded{...} everything wrapped between \expanded{ and the matching } shall be expanded totally. (An interesting aspect of \expanded is that the matching } may come into being in the course of \expanded-driven expansion.)

But the total expansion of an expression \unexpanded{...} is just the tokens wrapped between \unexpanded{ and the matching }.

The kind of argument processed by \expanded and \unexpanded and some other TeX-primitives is called ⟨general text⟩ in these sections of the TeXbook where the grammar of the typesetting language TeX is presented in a modified form of the Backus-Naur-notation. A subtlety with TeX is: While searching for the first { of a ⟨general text⟩ expandable tokens are expanded.

Thus with

\unexpanded\expandafter{%
   \romannumeral\Expandtimes{2}\AuthorAndTitle[2]%
}%

during the search for the first { of \unexpanded's ⟨general text⟩ expansion of \expandafter is triggered , which in turn triggers expansion of \romannumeral, which in turn triggers and keeps going an expansion-cascade whose first expansion-step is expanding \Expandtimes, and where the result, which the focus of all the expansion-work lays on, is producing an amount of "hits" on the place right behind \Expandtimes{2}—before the first "hit" by \expandafter that place is the token \AuthorAndTitle—, which corresponds to the value of the TeX ⟨number⟩ quantity provided as \Expandtime's argument. In this scenario that is two "hits" by \expandafter as that amount of "hits" by \expandafter is needed for obtaining the result of \AuthorAndTitle[2].
(This can be found out, e.g., by "lookig" at the definition of \AuthorAndTitle via \show\AuthorAndTitle.)
So in this scenario the token \romannumeral right before the token \Expandtimes actually is (ab?)used for keeping an expansion-cascade going. That expansion-cascade in turn is arranged so that when the expansion-work, which the focus lays on, is done, TeX encounters a TeX ⟨number⟩ quantity whose value is not positive and thus TeX just gobbles the tokens forming that TeX ⟨number⟩ quantity and terminates \romannumeral-driven expansion without delivering any tokens representing lowercase roman notation for them in return.

Basically the variant of \Expandtimes given with this example appends three digits 0 to a TeX ⟨number⟩ quantity provided as \Expandtime's argument and via \number\number converted into a sequence of digit-character tokens so that it is multiplied by the factor 1000 and then via another \romannumeral, not the \romannumeral before \Expandtimes but the \romannumeral before \number\number within the ⟨definition text⟩ of the definition of \Expandtimes, converts the result of multiplication into lowercase roman notation so that you get an amount of m which corresponds to the value of the TeX ⟨number⟩ quantity before multiplying it by 1000. Two \number are needed to ensure the space between \number\number#1 and 000 is removed in case #1 is, e.g., a \countdef-token whereafter TeX's algorithm for gathering tokens belonging to a TeX ⟨number⟩ quantity does not remove a trailing space as it would do in case (modulo expansion of expandable tokens) the tokens belonging to the TeX ⟨number⟩ quantity in question formed a sequence of digit-character-tokens.
On the other hand that space is needed in case #1 is s.th. like \count12 where you don't want TeX to take 12000 for the number of the \count-register.
Then iteration on these m takes place—before the first iteration with an empty argument—where in each iteration the amount of \expandafter the argument contains is doubled and another \expandafter is appended so that by and by you get as many \expandafter-chains as needed for having an amount of hits by \expandafter on the place following \Expandtimes{2} which corresponds to the value of the TeX ⟨number⟩ quantity which forms \Expandtime's argument..

5
  • Is this expand and unexpand and viceversa and again really necessary. Is the proposed \edef\x{\noexpand\fancyhead[L]{{#1 -- \z@author, \z@title}}}\x not enough? BTW: would you mind to explain the proposed "scavenger hunt" through the expand and unexpand forest?
    – wolfrevo
    Oct 21, 2023 at 8:15
  • @wolfrevo this depends on the contents of the input. If they are safe to be used in a full expansion then yes, the proposed \edef is enough. But there is stuff that might break in that usage, but work in the proposed solution here with only exactly two steps of expansion.
    – Skillmon
    Oct 21, 2023 at 11:59
  • \@wolfrevo I added some remarks. Oct 22, 2023 at 1:00
  • @UlrichDiez: Thanks a lot!
    – wolfrevo
    Oct 23, 2023 at 9:37
  • I accepted this answer because it addresses my main question: expansion.
    – wolfrevo
    Oct 23, 2023 at 9:46
3

To begin with, I don't think that something like

\mychapter{{arg1},{arg2},{arg3},{arg4},{arg5},{arg6},{arg7},{arg8},{arg9},{arg10}}

is going to be maintainable and I advise you to input such things with a key-value syntax. You just prepare a template like

\mychapter{
  author=,
  title=,
  foo=,
  bar=,
  x=,
  y=,
  z=,
}

and when you need to type in the data for a chapter you paste the template and fill it in, and you know what piece of data goes where.

\documentclass{book}

\usepackage{lipsum}
\usepackage{fancyhdr}

\pagestyle{fancy}
\fancyhf{}
\fancyhead[RE]{\thepage}
\fancyhead[LO]{\thepage}

% no chapter header
\makeatletter
\def\@makechapterhead#1{}
\makeatother

\ExplSyntaxOn

\NewDocumentCommand{\mychapter}{m}
 {
  \wolfrevo_chapter:n { #1 }
 }

\keys_define:nn { wolfrevo/chapter }
 {
  author .tl_set:N = \l_wolfrevo_chapter_author_tl,
  title  .tl_set:N = \l_wolfrevo_chapter_title_tl,
  foo    .tl_set:N = \l_wolfrevo_chapter_foo_tl,
  bar    .tl_set:N = \l_wolfrevo_chapter_bar_tl,
  x      .tl_set:N = \l_wolfrevo_chapter_x_tl,
  y      .tl_set:N = \l_wolfrevo_chapter_y_tl,
  z      .tl_set:N = \l_wolfrevo_chapter_z_tl,
 }

\tl_new:N \l_wolfrevo_chapter_clear_tl
\keys_precompile:nnN { wolfrevo/chapter }
 {
  author=,
  title=,
  foo=,
  bar=,
  x=,
  y=,
  z=,
 }
 \l_wolfrevo_chapter_clear_tl

\cs_new_protected:Nn \wolfrevo_chapter:n
 {
  % first clear all fields
  \tl_use:N \l_wolfrevo_chapter_clear_tl
  % absorb the values
  \keys_set:nn { wolfrevo/chapter } { #1 }
  % set the header with author and title
  \__wolfrevo_chapter_start:VV \l_wolfrevo_chapter_title_tl \l_wolfrevo_chapter_author_tl
  % do whatever you need with the other keys
  % [...]
 }

\cs_new_protected:Nn \__wolfrevo_chapter_start:nn
 {% start a chapter: #1 is the title, #2 is the author
  \chapter{#1}
  \thispagestyle{fancy}
  \fancyhead[LE]{#2}
  \fancyhead[RO]{#1}
 }
\cs_generate_variant:Nn \__wolfrevo_chapter_start:nn { VV }

\ExplSyntaxOff

\begin{document}

\mychapter{
  author={Author 1},
  title={Title 1},
}
\lipsum[1-10]

\mychapter{
  author={Author 2},
  title={Title 2}
}
\lipsum[2-10]

\end{document}

The idea is that \mychapter collects the arguments (with the easier key-value syntax) and then delegates the various tasks to auxiliary functions. In this case \__wolfrevo_chapter_start:VV which will use the explicit actual values you gave and not the macros storing them (this is the main problem in your code).

enter image description here

enter image description here

enter image description here

enter image description here

Can you do it as you propose? Yes, with simple changes.

\documentclass{book}

\usepackage{lipsum}
\usepackage{fancyhdr}

\pagestyle{fancy}
\fancyhf{}
\fancyhead[RE]{\thepage}
\fancyhead[LO]{\thepage}

% no chapter header
\makeatletter
\def\@makechapterhead#1{}
\makeatother

\ExplSyntaxOn

\NewDocumentCommand{\mychapter}{m}
 {
  \wolfrevo_chapter:n { #1 }
 }

\cs_new_protected:Nn \wolfrevo_chapter:n
 {
  % set the header with author and title
  \__wolfrevo_chapter_start:ee { \clist_item:nn { #1 } { 1 } } { \clist_item:nn { #1 } { 2 } }
  % do whatever you need with the other keys
  % [...]
 }

\cs_new_protected:Nn \__wolfrevo_chapter_start:nn
 {% start a chapter: #1 is the title, #2 is the author
  \chapter{#1}
  \thispagestyle{fancy}
  \fancyhead[LE]{#2}
  \fancyhead[RO]{#1}
 }
\cs_generate_variant:Nn \__wolfrevo_chapter_start:nn { ee }

\ExplSyntaxOff

\begin{document}

\mychapter{{Author 1},{Title 1},{foo1},{bar1},{x1},{y1},{z1}}

\lipsum[1-10]

\mychapter{{Author 2},{Title 2},{foo2},{bar2},{x2},{y2},{z2}}

\lipsum[2-10]

\end{document}
4
  • Personally, I think a key-value, as proposed here by egreg, is the better solution. It has a much clearer syntax and you can't mess things up if you change the order of the input. The latter would be a problem with a clist. But since the OP especially asked for a clist solution, he might've further reasons for that. If not, that should be the accepted answer.
    – lukeflo
    Oct 21, 2023 at 13:11
  • 1
    @lukeflo I added how to do it without key-value.
    – egreg
    Oct 21, 2023 at 13:34
  • \@lukeflo: Thanks for your example which is very useful for me. I accepted @UlrichDiez answer because it addresses my main question: expansion.
    – wolfrevo
    Oct 23, 2023 at 9:44
  • @egreg After studying your example more carefully I understand the advantages of using key-value syntax. The code is clearer and easy to understand and maintain. Thank you!
    – wolfrevo
    Nov 2, 2023 at 10:30
2

This is similar in spirit to @egreg's answer, just showing a different key=value interface. The one in use here is expkv-cs with the hash-variant.

To access a key value you simply use \ekvcValue{<name>}{#1} in your macro definition, if you want to forward a key's value to another macro you can use \ekvcValueSplit{<name>}{#1}{<code getting the value>}.

The used mechanism works completely without assignments to some temporary macros, hence you don't have to use any expansion control with this. For instance, instead of doing \ekvcValueSplit{title}{#1}\chapter you could also use \chapter{\ekvcValue{title}{#1}} and it would correctly work out (the moving argument would just get more contents than it really needs, leading to slightly worse performance, but the difference would be negligible and no user would notice).

\documentclass[twoside]{book}

\usepackage{lipsum}
\usepackage{fancyhdr}

\usepackage{expkv-cs}

\ekvcHash\mychapter
  {
    author=,
    title=,
    key=non-empty default,
  }
  {%
    \ekvcValueSplit{title}{#1}\chapter
    \ekvcValueSplit{author}{#1}{\fancyhead[LE]}%
    \ekvcValueSplit{title}{#1}{\fancyhead[RO]}%
    \thispagestyle{fancy}%
    % just to show another key being used
    Something using \ekvcValue{key}{#1}.\par
  }

\makeatletter
\def\@makechapterhead#1{}
\makeatother

\pagestyle{fancy}
\fancyhf{}
\fancyfoot[C]{\thepage}

\begin{document}

\mychapter{author={Author 1},title={Title 1}}
\lipsum[1-10]
\mychapter{author=Author 2,key=awesome example,title=Title 2}
\lipsum[2-10]

\end{document}

If you wanted to use less than or equal to 9 arguments I'd suggest using the split-variant of expkv-cs, which is even easier to use. In your example with only two keys you'd just do

\ekvcSplit\mychapter
  {
    author=, % will be #1
    title=   % will be #2
  }
  {%
    \chapter{#2}%
    \fancyhead[LE]{#1}%
    \fancyhead[RO]{#2}%
    \thispagestyle{fancy}%
  }

What might turn out simpler to use than the hash-variant, and still supporting more than 9 keys, is to use multiple stages of the split-variant. This becomes relatively easy to code using the special ...-notation in the key-definition lists. If you specified ... the macro will not complain about unknown keys, but store as a key=value list in a parameter. You can then forward that remaining list to another macro. Note that the values you split out this way will not be part of that list.

\makeatletter
\ekvcSplit\mychapter
  {
     author = % #1
    ,title  = % #2
    ,...      % all unknown keys will be grouped as #3
  }
  {%
    \chapter{#2}%
    \fancyhead[LE]{#1}%
    \fancyhead[RO]{#2}%
    \thispagestyle{fancy}%
    \mychapter@rest{#3}%
  }
\ekvcSplit\mychapter@rest
  {
     key   = non-empty default
    ,other = another key
  }
  {Something using #1 and #2.\par}
\makeatother

Yet another possibility using the split-variants would be to use multiple split macros in parallel without forwarding the unknows, but just silently ignoring them. Note that this might get so complicated that you're better of with expansion-control requiring solutions, e.g. those of the other answers, or using expkv-def.

\makeatletter
\begingroup
% temporary helper macros
\def\mychapter@setupkey#1{\long\ekvdef{mychapter}{#1}{}}
\def\mychapter@setupnoval#1{\ekvdefNoVal{mychapter}{#1}{}}
\def\mychapter@setupunknowns#1%
  {\long\ekvdefunknown{\string#1}{}\long\ekvdefunknownNoVal{\string#1}{}}
\expanded\expanded{{%
\endgroup
% setup all the known keys in this list
\ekvcsvloop\mychapter@setupkey{author, title, key, other}%
% setup all the known keys that don't take a value in this list
\ekvcsvloop\mychapter@setupnoval{}%
% setup all macros that should accept kv-input and ignore unknown keys here
\ekvcsvloop\mychapter@setupunknowns{\mychapter@head, \mychapter@rest}
}}
\newcommand\mychapter[1]
  {%
    % parse through the list once to check for any unknown key
    \ekvset{mychapter}{#1}%
    \mychapter@head{#1}%
    \mychapter@rest{#1}%
  }
\ekvcSplit\mychapter@head
  {
     author = % #1
    ,title  = % #2
  }
  {%
    \chapter{#2}%
    \fancyhead[LE]{#1}%
    \fancyhead[RO]{#2}%
    \thispagestyle{fancy}%
  }
\ekvcSplit\mychapter@rest
  {
     key   = non-empty default
    ,other = another key
  }
  {Something using #1 and #2.\par}
\makeatother
5
  • \@Skillmon: Thanks for your answer and the valuable reference to expkv-cs. I accepted @UlrichDiez answer because it addresses my main question: expansion.
    – wolfrevo
    Oct 23, 2023 at 9:42
  • @wolfrevo fine by me, glad you found the answer interesting. Just an aside: This answer solves your expansion problem as well, by rendering it completely unnecessary to do expansion control... :P
    – Skillmon
    Oct 23, 2023 at 9:56
  • \@Skillmon: Actually I am evaluating your answer. There are lots of pros. But I'm not able to pass arguments containing \par or empty lines. I'm afraid it is not possible. I'll post another question on this.
    – wolfrevo
    Oct 23, 2023 at 19:13
  • 1
    @wolfrevo put a long infront of the key that should accept a \par. E.g. use \ekvcSplit\foo{long bar=baz}{Something using #1} and the key bar will accept \par.
    – Skillmon
    Oct 23, 2023 at 19:22
  • \@Skillmon: Thanks a lot!
    – wolfrevo
    Oct 23, 2023 at 21:10

You must log in to answer this question.

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