可変長引数マクロ(expl3)

可変長引数マクロ(expl3)

- 土 村 の投稿
返信数: 6
tabular 環境の & 区切りのデータを加工したくて、引数の個数を自由にとるマクロを作ろうと思いました。expl3 で seq を使えばできそうな気がしたので、にわかに勉強し始めたのですが、& が副作用を持っているようで、太刀打ちできません。どなたかご指導いただけませんでしょうか。

\documentclass{article}
\usepackage{expl3}
\begin{document}

\ExplSyntaxOn
\seq_new:N \l_row_seq

\newcommand{\titlerow}[1]{ % 失敗パターン1
\seq_set_split:Nnn \l_row_seq {&} {#1}
\seq_pop:NN \l_row_seq \i % 先頭要素を \i に
\textbf{\i} % 先頭のみ & なしで出力
\seq_map_variable:NNn \l_row_seq \j { & \textbf{\j} } % NG: \j が変数でなくなる
}

\newcommand{\TITLEROW}[1]{ % 失敗パターン2
\seq_set_split:Nnn \l_row_seq {&} {#1}
\seq_pop_right:NN \l_row_seq \i % 末尾要素を \i に退避
\seq_map_variable:NNn \l_row_seq \j { \textbf{\j} & }
\textbf{\i} % NG: \i が変数でなくなる
}
\ExplSyntaxOff

\begin{tabular}{|l|l|l|l|l|l|l|}
my goal & \textbf{1} & \textbf{2} & \textbf{3} & \textbf{4} & \textbf{5} \\
titlerow & \titlerow{1 & 2 & 3 & 4 & 5} \\
TITLEROW & \TITLEROW{1 & 2 & 3 & 4 & 5} \\
\end{tabular}
\end{document}

\j や \i が、& の後ろにくると変数の効力をなくして、ドットなしの i や j の文字に戻ってしまいます。& を別の文字に置き換えると、望み通り、\textbf{5} のようになります。どうしたら & の副作用(?)を打ち消せるでしょうか。

TeXLive 2017 / 2020 の platex / pdflatex いずれも同じ動作です。
土 村 への返信

Re: 可変長引数マクロ(expl3)

- 前田 一貴 の投稿
1の方に限って言えば,locally に \j に代入しているのが問題なので,\seq_map_inline:Nn を使えばよさそうです.
2はどうすればよいのだろう? もちろん,\xdef\i{\i} とか入れちゃえばなんとかなりますが…….

\documentclass{article}
\usepackage{expl3}
\begin{document}

\ExplSyntaxOn
\seq_new:N \l_row_seq

\newcommand{\titlerow}[1]{ % 失敗パターン1
\seq_set_split:Nnn \l_row_seq {&} {#1}
\seq_pop:NN \l_row_seq \i % 先頭要素を \i に
\textbf{\i} % 先頭のみ & なしで出力
\seq_map_inline:Nn \l_row_seq { & \textbf{##1} } % OK
}

\newcommand{\TITLEROW}[1]{ % 失敗パターン2
\seq_set_split:Nnn \l_row_seq {&} {#1}
\seq_pop_right:NN \l_row_seq \i % 末尾要素を \i に退避
% \xdef\i{\i}
\seq_map_inline:Nn \l_row_seq  { \textbf{##1} & }
\textbf{\i} % NG: \i が変数でなくなる
}
\ExplSyntaxOff

\begin{tabular}{|l|l|l|l|l|l|l|}
my goal & \textbf{1} & \textbf{2} & \textbf{3} & \textbf{4} & \textbf{5} \\
titlerow & \titlerow{1 & 2 & 3 & 4 & 5} \\
TITLEROW & \TITLEROW{1 & 2 & 3 & 4 & 5} \\
\end{tabular}
\end{document}
前田 一貴 への返信

Re: 可変長引数マクロ(expl3)

- 前田 一貴 の投稿
これが一番きれいかも.

\newcommand{\TITLEROW}[1]{
\seq_set_split:Nnn \l_tmpa_seq {&} {#1}
\seq_set_map:NNn \l_tmpb_seq \l_tmpa_seq  { \textbf{##1} }
\seq_use:Nn \l_tmpb_seq {&}
}
前田 一貴 への返信

Re: 可変長引数マクロ(expl3)

- 土 村 の投稿
ありがとうございます!片方でも直せればOKなので、これで大丈夫です。
\seq_map_inline:Nn も試していたのですが、##1 には気づきませんでした。#1 としたままで、外側のマクロの引数を使い続けていました。
土 村 への返信

Re: 可変長引数マクロ(expl3)

- 帯田 木偶太 の投稿
expl3 については全く知識がないので意図を理解できているか
自信がないのですが、以下のようなことをなさりたいのでしょうか?

        \documentclass{article}\relax
        \makeatletter
                \newcommand*\titlerow[1]{%
                        \let\reserved@a=\@empty
                        \@for\reserved@b:=#1\do{%
                                \ifx \reserved@a \@empty
                                        \toks@={}%
                                \else
                                        \toks@=\expandafter{\reserved@a&}%
                                \fi
                                \@temptokena=\expandafter{\reserved@b}%
                                \edef\reserved@a{%
                                        \the\toks@\noexpand\textbf{\the\@temptokena}%
                                }%
                        }%
                        \reserved@a
                }
        \makeatother
        \begin{document}
        \begin{tabular}{llllll}
                \titlerow{bold 1,bold 2,bold 3,bold 4}\\
                \titlerow{bold 5,bold 6}\\
                medium 1 & medium 2 & medium 3
        \end{tabular}
        \end{document}

tabular 環境や array 環境の中での & は、グループの区切りとしても作用するので、
特定のセルでの代入は、次のセルまたは行に移動した途端、無効になってしまいます。
このこと自体は、expl3 を使っても、マクロ的なアプローチでは避け難かろうと
思います。
        ※  tabular 環境の内側先頭は、既に最初の行の最初のセルです。
            最初のセルに入る前に何かをするには、\noalign などを
            使って結構面倒なことをやる必要があるでしょう。

上記のマクロでは、行全体を一旦、スクラッチのマクロに収めたうえで
最後に一気に書き出すことで、これを回避しています。expl3 を利用する場合も、
同様の構成は可能ではなかろうかと。
帯田 木偶太 への返信

Re: 可変長引数マクロ(expl3)

- 土 村 の投稿
直していただいた通りで、狙った動作になっています。
実際には \textbf{} のところには \multicolumn{1}{c}{} なども加えるかもしれませんが、引数の分割は従来の枠組みでも可能なのですね。
ありがとうございました。
土 村 への返信

Re: 可変長引数マクロ(expl3)

- 帯田 木偶太 の投稿
カンマで分割するのは、LaTeX 標準の \@for でできます。
ただ、カンマ区切りしか扱えません。
区切り文字をオプション指定できるような拡張も北見 けんさんによって
        https://oku.edu.mie-u.ac.jp/~okumura/texfaq/qa/15266.html
に発表されていたりしますが、そのままではアンパサンド区切りは
エラーになるようです。\titlerow の定義をさらにいじって
アンパサンド区切りに対応させることもできましたが、
今度は \multicolumn を埋め込むことができなくなってしまいました。
※  多分、\multicolumn 自身がアラインメントをいじるので、それとの
    整合性がとれないんだろうと思いますが、この点の解決は私の手には
    負えません。

========================================================================

        \documentclass{article}\relax
        \makeatletter
                % \@for の拡張(by 北見 けんさん):ここから
                        \def\@for{\begingroup\@testopt\ex@for@ini,}
                        %\@testoptが、\reserved@a~d を変えてしまうのを隠蔽します。
                        %\@for は、\@for\reserved@b:=\list{\job}の形で使われたりするので。
                        \long\def\ex@for@ini[#1]#2:=#3\do{%
                        \endgroup
                        \long\def\ex@forloop##1#1##2\@@##3##4{%
                            \def##3{##1}%
                            \ifx##3\@nnil\ex@for@break\fi
                            ##4%
                            \ex@forloop##2\@@##3{##4}}%
                            %  この部分は、例えば区切り文字が[,]で与えられたとすると
                            %  \long\def\ex@forloop#1,#2\@@#3#4{%
                            %      \def#3{#1}%
                            %      \ifx#3\@nnil\ex@for@break\fi
                            %      #4%
                            %      \ex@forloop#2\@@#3{#4}}%
                            %  という定義をすることになります。
                            %  #3 が項目を入れるマクロで、#4 がジョブ、#1,#2 がリストです。
                        \expandafter\def\expandafter\@fortmp\expandafter{#3}%
                        \ifx\@fortmp\@empty\ex@for@break\fi
                        \expandafter\ex@forloop#3#1\@nil#1\@@#2}
                        \long\def\ex@for@break#1\@@#2#3{\fi}
                            %  このマクロは、下の例のように、
                            %  ジョブの中で \if と \fi の間に置けば、
                            %  ループを抜けるのに使えます。
                %  \@for の拡張:ここまで
                \let\AmpChar=&%
                \chardef\AmpCharCatCode=\catcode`\&
                \catcode`\&=12\relax
                \newcommand*\titlerow{%
                        \catcode`\&=12\relax
                        \titlerow@
                }
                \newcommand*\titlerow@[1]{%
                        \catcode`\&=\AmpCharCatCode
                        \let\reserved@a=\@empty
                        \@for[&]\reserved@b:=#1\do{%
                                \ifx \reserved@a \@empty
                                        \toks@={}%
                                \else
                                        \toks@=\expandafter{\reserved@a\AmpChar}%
                                \fi
                                \@temptokena=\expandafter{\reserved@b}%
                                \edef\reserved@a{%
                                        \the\toks@
                                        % \multicolumn を入れるとエラーになるので
                                        % ちょっとごまかし
                                                \hfill\noexpand\textbf{\the\@temptokena}%
                                                \hfill\null
                                }%
                        }%
                        \reserved@a
                }
                \catcode`\&=\AmpCharCatCode
        \makeatother
        \begin{document}
        \begin{tabular}{llllll}
                \titlerow{bold 1&bold 2&bold 3&bold 4}\\
                \titlerow{bold 5&bold 6}\\
                medium 1 & medium 2 & medium 3
        \end{tabular}
        \end{document}