从 \foreach 获取价值

从 \foreach 获取价值

我创建了一个名为的宏\fromlist来从列表中获取值。该值可以排版,但不能直接调用,为什么以及如何处理?

代码:

\documentclass[a4paper]{article}
\usepackage{pgffor}
\usepackage{geometry}
\geometry{showframe}
\geometry{left=1cm,right=1cm,top=1cm,bottom=1cm}
\parindent0pt


\begin{document}

\newcommand\fromlist[2]{%
  \foreach \x [count=\i] in #1{%
    \ifnum\i=#2 \x\breakforeach\fi%
  }%
}

% Test:
\def\bbb{2in}\rule{\bbb}{5pt} % This ok.
\def\bbb{\fromlist{{1in,2in,3in}}{2}}\bbb %This is ok.
\rule{\fromlist{{1in,2in,3in}}{2}}{5pt} % This causes error, why?

答案1

在此处输入图片描述

您需要通过扩展来提取项目,您不能在 的参数内进行赋值\rule。幸运的是,LaTeX 在clist(comma list) 模块中提供了这样一个现成的功能。更确切地说,nn变体是可用的,它适用于显式列表,如果您需要扩展包含列表的命令,您可以像ee这里第二个示例那样声明一个变体。

\documentclass[a4paper]{article}
\usepackage{geometry}
\geometry{showframe}
\geometry{left=1cm,right=1cm,top=1cm,bottom=1cm}
\parindent0pt

\ExplSyntaxOn
\cs_generate_variant:Nn\clist_item:nn{ee}
\newcommand\fromlist{\clist_item:ee}
\ExplSyntaxOff

\newcommand\mylist{1in,2in,3in}
\begin{document}


\rule{\fromlist{1in,2in,3in}{2}}{5pt} 

\rule{\fromlist{\mylist}{3}}{5pt} 


\end{document}

答案2

当我们说“一个命令是(完全)可扩展的”时,我们的意思是它通过不执行任何赋值或排版指令(其中包括\relax)来提供预期的结果。

所以你的\bbb作品,因为它的扩张是长度。TeX 知道 的第一个参数\rule应该包含一个(固定的)长度,它会执行宏扩展,直到找到符合要求的内容。

当 TeX 看到你的 时\fromlist,它会意识到它不是一个长度,所以它会进行扩展并看到\foreach。如果你这样做

latexdef -p pgffor -s foreach

从终端,你会得到

% pgffor.code.tex, line 60:
\let\foreach=\pgffor@foreach

% pgffor.code.tex, line 45:
\def\pgffor@foreach{%
    \pgffor@atbeginforeach%
    \let\pgffor@assign@before@code=\pgfutil@empty%
    \let\pgffor@assign@after@code=\pgfutil@empty%
    \let\pgffor@assign@once@code=\pgfutil@empty%
    \let\pgffor@remember@code=\pgfutil@empty%
    \let\pgffor@remember@once@code=\pgfutil@empty%
    \pgffor@alphabeticsequencefalse%
    \pgffor@contextfalse%
    %
    \let\pgffor@var=\pgfutil@empty
    %
    \pgffor@vars%
}

你知道你的计划注定要失败,因为即使\pgffor@atbeginforeach它是可扩展的(实际上并非如此),接下来还会有一堆\let指令,而一个已经太多了。

是否需要运行latexdef才能查看命令是否可扩展?其实不需要。一般来说,除非有人告诉您命令是可扩展的,否则您不应该期望命令是可扩展的。

在这些expl3函数中,您可以发现那些(完全)可扩展的函数,因为它们的名称后面有一个星号(实心或空心)。描述位于interface3.pdf

您的输入是一个逗号分隔的项目列表,因此它符合 的语法clist。是的,该函数\clist_item:nn后面有一个星号!因此您可以这样做

\ExplSyntaxOn
\cs_set_eq:NN \fromlist \clist_item:nn
\ExplSyntaxOff

但你会问一个新问题,因为

\def\ccc{1pt,2pt,3pt}
\rule{\fromlist\ccc{2}}{3cm}

不起作用。如何同时容纳隐式和显式逗号列表?

我的建议是\fromlist使用接受隐式列表的 *-variant 来定义:

\ExplSyntaxOn
\NewExpandableDocumentCommand{\fromlist}{smm}
 {
  \IfBooleanTF{#1}
   {% there is *
    \clist_item:Nn #2 { #3 }
   }
   {% no *
    \clist_item:nn { #2 } { #3 }
   }
 }
\ExplSyntaxOff

一个完整的例子。

\documentclass{article}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\fromlist}{smm}
 {
  \IfBooleanTF{#1}
   {% there is *
    \clist_item:Nn #2 { #3 }
   }
   {% no *
    \clist_item:nn { #2 } { #3 }
   }
 }

\ExplSyntaxOff

\newcommand{\mylist}{1in,2in,3in}

\begin{document}

\rule{\fromlist{1in,2in,3in}{2}}{5pt}

\rule{\fromlist*{\mylist}{3}}{5pt}

\end{document}

在此处输入图片描述

答案3

正如我所评论的,\fromlist是不可扩展的(例如,尝试\edef\tmp{\fromlist{{1in,2in,3in}}{2}})。因此,\rule看不到 的最终结果\fromlist,无法用作其输入。因此它会引发错误。

下面提出了一种不同的方法,使用listofitems来读取和解析列表。然后,可以可扩展地调用列表中的特定条目。关键是在将列表元素传递给 之前读取列表\rule

我首先将其展示为一个两步过程,然后\readlist\rule。或者,也可以将这两个步骤放在宏中\Zrule,就像我在第二种方法中所做的那样。

\documentclass[a4paper]{article}
\usepackage{listofitems}
\usepackage{geometry}
\geometry{showframe}
\geometry{left=1cm,right=1cm,top=1cm,bottom=1cm}
\parindent0pt
\newcommand\Zrule[3]{%
  \readlist\rulearg{#1}%
  \rule{\rulearg[#2]}{#3}
}
\begin{document}

\readlist\rulelen{1cm,2.4cm,3cm}
\rule{\rulelen[2]}{5pt}

\Zrule{1in,2in,3in}{2}{5pt}
\end{document}

在此处输入图片描述

答案4

这是一个变体

\fromlist{⟨tokens to prepend to i-th comma-list-item⟩}%
         {⟨tokens to append to i-th comma-list-item⟩}%
         {⟨TeX-⟨number⟩-quantity of value i⟩}%
         {⟨comma-list⟩}

在当地范围内第 1 次\foreach迭代—

⟨tokens to prepend to i-th comma-list-item⟩{⟨i-th comma-list-item⟩}⟨tokens to append to i-th comma-list-item⟩

\documentclass[a4paper]{article}
\usepackage{pgffor}
\usepackage{geometry}
\geometry{showframe}
\geometry{left=1cm,right=1cm,top=1cm,bottom=1cm}
\parindent0pt

\makeatletter
\newcommand\PassFirstToSecond[2]{#2{#1}}
\newcommand\fromlist[4]{%
  \foreach \x [count=\i] in #4{%
    \ifnum\i=\expandafter\@firstofone\expandafter{\number#3} %
    \expandafter\@firstofone\else\expandafter\@gobble\fi
    {%
      \breakforeach
      \expandafter\PassFirstToSecond\expandafter{\x}{#1}#2%
    }%
  }%
}%
\makeatother

\begin{document}

List provided explicitly:

\fromlist{\rule}{{5pt}}{1}{{1in,2in,3in}}

\fromlist{\rule}{{5pt}}{2}{{1in,2in,3in}}

\fromlist{\rule}{{5pt}}{3}{{1in,2in,3in}}

\newcommand\commalist{1in,2in,3in}

List provided via macro:

\fromlist{\rule}{{5pt}}{1}{\commalist}

\fromlist{\rule}{{5pt}}{2}{\commalist}

\fromlist{\rule}{{5pt}}{3}{\commalist}

\end{document}

在此处输入图片描述

相关内容