总结

总结

我想实现一个简单的 foreach 循环,它接受两个参数 - 一个字符串和每个字符的预期格式(最后一个字符除外),如下所示:

\spell{example}{\x-},其产生输出:e-x-a-m-p-l-e

我发现自己是 TeX 的新手,对 TeX 的具体细节并不熟悉,因此搜索 MWE 并没有成功。以下示例让我陷入了错误(已编辑)

更新:我已经更新了代码,现在可以正常工作了。但是,下面的各种答案也提供了更好的方法。

\documentclass{article}

\makeatletter
\newcommand{\spell}[2]{
  \@tfor\x:=#1\do{\x#2}
}
\makeatletter

\begin{document}
\spell{example}{-}
\end{document}

答案1

我在这里只看到 Expl3 示例,但此任务非常适合仅使用 TeX 基元。我们定义宏,\spell该宏获取单个单词并按照 给出的格式打印第一个字母\spellFirst,按照 给出的格式打印下一个字母\spellNext。我们只需要五行非常清晰的代码。另一方面,在我看来,Expl3 示例并不清晰。

\def\spell #1{\spellA#1{}}
\def\spellA #1{\spellFirst{#1}\spellB}
\def\spellB #1{\ifx^#1^\else\spellNext{#1}\expandafter\spellB\fi}

% format is given by:
\def\spellFirst #1{#1}  % only copy to the output
\def\spellNext  #1{-#1} % add `-` before the character

\spell{example}.

\bye

答案2

以下实现了两者。包含最后一个字符的变体直接使用函数即可expl3\text_map_inline:nn忽略最后一个字符的变体需要循环文本两次(首先计算长度,然后应用用户函数)。

\documentclass{article}

\ExplSyntaxOn
\int_new:N \l__chzzh_grapheme_count_int
\int_new:N \l__chzzh_current_int
\NewDocumentCommand \chzzhLoop { s m m }
  {
    \IfBooleanTF {#1}
      { \__chzzh_loop_without_last:en { \text_expand:n {#2} } {#3} }
      { \text_map_inline:nn {#2} {#3} }
  }
\cs_new:Npn \__chzzh_loop_without_last:nn #1#2
  {
    \int_zero:N \l__chzzh_grapheme_count_int
    \int_zero:N \l__chzzh_current_int
    \text_map_inline:nn {#1} { \int_incr:N \l__chzzh_grapheme_count_int }
    \text_map_inline:nn {#1}
      {
        \int_incr:N \l__chzzh_current_int
        \int_compare:nNnTF \l__chzzh_current_int < \l__chzzh_grapheme_count_int
          {#2}
          {##1}
      }
  }
\cs_generate_variant:Nn \__chzzh_loop_without_last:nn { e }
\ExplSyntaxOff

\begin{document}
\chzzhLoop{example}{#1-}

\chzzhLoop*{example}{#1-}
\end{document}

在此处输入图片描述

答案3

总结

如果您只想在字素之间插入连字符,这是可行的。

\documentclass{article}
\usepackage{iftex}

\iftutex
  \usepackage{fontsetup}
\fi

\ExplSyntaxOn

\NewDocumentCommand{\unicodespell}{m}
 {
  \seq_clear:N \l_tmpa_seq
  \text_map_inline:nn { #1 } { \seq_put_right:Nn \l_tmpa_seq { ##1 } }
  \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl
  \tl_use:N \l_tmpa_tl
  \seq_map_inline:Nn \l_tmpa_seq { -##1 }
 }

\ExplSyntaxOff

\begin{document}

\iftutex
  \unicodespell{éxãm̃ple}% can't work correctly in pdflatex
\else
  \unicodespell{éxãmple}% but this works in pdflatex
\fi

\end{document}

输出pdflatex

在此处输入图片描述

笔记。éxãmple这里用的是单词(pdflatex无法处理组合字符)。

输出与xelatexlualatex

在此处输入图片描述

笔记。在这里您可以看到,即使是 U+006D LATIN SMALL LETTER M 与 U+0303 COMBINING TILDE 的组合,也能被正确处理。

与 wipet 的解决方案进行比较,后者声称可以使用 Unicode。使用 进行编译optex

\fontfam[Termes]

\def\spell #1{\spellA#1{}}
\def\spellA #1{\spellFirst{#1}\spellB}
\def\spellB #1{\ifx^#1^\else\spellNext{#1}\expandafter\spellB\fi}

% format is given by:
\def\spellFirst #1{#1}  % only copy to the output
\def\spellNext  #1{-#1} % add `-` before the character

\spell{éxãm̃ple}% this doesn't work

\bye

在此处输入图片描述

原始答案包含更多详细信息

在每个项目之间插入一些标记很容易:分离给定字符串中的第一个项目,然后通过在每个项目前面添加第二个参数来处理其余项目。

最后,可选择添加第二个参数。如果要交换*-variant 的行为,只需更改\IfBooleanT\IfBooleanF

下面的例子表明,这也适用于单字母的单词,甚至第一个参数为空。

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\spell}{smm}
 {
  \tl_head:n { #2 }
  \tl_map_inline:en { \tl_tail:n { #2 } } { #3 ##1 }
  \IfBooleanF{#1}{#3}
 }
\cs_generate_variant:Nn \tl_map_inline:nn { e }

\ExplSyntaxOff

\begin{document}

\spell*{example}{-}

\spell{example}{-}

\spell*{example}{=}

\spell*{e}{-} \spell{e}{-}

X\spell{}{-}X

\end{document}

在此处输入图片描述

然而,如果您希望对项目进行更通用的格式化,比如\fbox对每个项目进行格式化,您需要做更多的事情。

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\spell}{smm}
 {
  \chzzh_spell:nnn { #1 } { #2 } { #3 }
 }

\cs_generate_variant:Nn \tl_map_function:nN { e }

\cs_new_protected:Nn \chzzh_spell:nnn
 {
  \cs_set_protected:Nn \__chzzh_spell_item:n { #3 }
  \tl_map_function:eN { \tl_range:nnn { #2 } { 1 } { -2 } } \__chzzh_spell_item:n
  \bool_if:nTF { #1 }
   {
    \tl_item:nn { #2 } { -1 }
   }
   {
    \__chzzh_spell_item:e { \tl_item:nn { #2 } { -1 } }
   }
 }
\cs_new_protected:Nn \__chzzh_spell_item:n {} % initialize
\cs_generate_variant:Nn \__chzzh_spell_item:n { e }

\ExplSyntaxOff

\begin{document}

\spell{example}{#1-}

\spell*{example}{#1-}

\spell{example}{\fbox{#1}}

\end{document}

此处分离的项目是最后一个。

在此处输入图片描述

但是,如果您的文本预计包含非 ASCII 字符,则需要更多内容;在帮助下,\text_map_inline:nn我们可以填充序列并对其采取行动。

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\spell}{smm}
 {
  \chzzh_spell:nnn { #1 } { #2 } { #3 }
 }

\cs_generate_variant:Nn \tl_map_function:nN { e }

\seq_new:N \l__chzzh_spell_seq
\tl_new:N \l__chzzh_spell_last_tl

\cs_new_protected:Nn \chzzh_spell:nnn
 {
  % populate the sequence with the items in #2
  \seq_clear:N \l__chzzh_spell_seq
  \text_map_inline:nn { #2 }
   {
    \seq_put_right:Nn \l__chzzh_spell_seq { ##1 }
   }
  % detach the last item
  \seq_pop_right:NN \l__chzzh_spell_seq \l__chzzh_spell_last_tl
  % define the aux function like specified by the last argument
  \cs_set_protected:Nn \__chzzh_spell_item:n { #3 }
  % map the sequence
  \seq_map_function:NN \l__chzzh_spell_seq  \__chzzh_spell_item:n
  % deal with the last item
  \bool_if:nTF { #1 }
   {
    \tl_use:N \l__chzzh_spell_last_tl
   }
   {
    \__chzzh_spell_item:V \l__chzzh_spell_last_tl
   }
 }
\cs_new_protected:Nn \__chzzh_spell_item:n {} % initialize
\cs_generate_variant:Nn \__chzzh_spell_item:n { V }

\ExplSyntaxOff

\begin{document}

\spell{example}{#1-}

\spell*{example}{#1-}

\spell{éxãmple}{\fbox{#1}}

\end{document}

在此处输入图片描述

相关内容