在命令中存储字符串数组

在命令中存储字符串数组

我想将三个字符串存储在一个变量中,\mydata如下所示:

\storedata\mydata{one}{two}{three}

然后我想通过以下方式提取第一个/第二个/第三个字符串:

\getdata[1]\mydata   % returns one
\getdata[2]\mydata   % returns two
\getdata[3]\mydata   % returns three

我该如何定义这些命令?

\newcommand\storedata[4] { \newcommand#1[?] ??? }
\newcommand\getdata[?] ???

答案1

\csname这是...操作的经典任务\endcsname。您将控制序列定义\data:\string\mydata:1为第一个参数、\data:\string\mydata:2第二个参数等。

请注意,我的解决方案使用了您建议的另一种语法,\storedata因为我们需要知道参数列表在哪里结束。

\newcount\tmpnum
\def\storedata#1#2{\tmpnum=0 \edef\tmp{\string#1}\storedataA#2\end}
\def\storedataA#1{\advance\tmpnum by1
   \ifx\end#1\else
      \expandafter\def\csname data:\tmp:\the\tmpnum\endcsname{#1}%
      \expandafter\storedataA\fi
}
\def\getdata[#1]#2{\csname data:\string#2:#1\endcsname}

\storedata\mydata{{one}{two}{three}}

A:\getdata[1]\mydata   % returns one
B:\getdata[2]\mydata   % returns two
C:\getdata[3]\mydata   % returns three

\bye

编辑:您的自我回答表明您只需要具有三个数据参数的宏\storedata。 然后使用原始的简单实现\ifcase是:

\def\storedata#1#2#3#4{\def#1{\or#2\or#3\or#4}}
\def\getdata[#1]#2{\ifcase\expandafter#1#2\else\outofrange\fi}
\def\outofrange{\errmessage{\string\getdata: out of range 1..3}}

答案2

实现expl3

使用 实现expl3;请注意,使用姓名而不是储料箱的控制序列。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\storedata}{mm}
  {
   \bcp_store_data:nn { #1 } { #2 }
  }

\DeclareExpandableDocumentCommand{\getdata}{O{1}m}
 {
  \bcp_get_data:nn { #1 } { #2 }
 }

\cs_new_protected:Npn \bcp_store_data:nn #1 #2
 {
  % create the sequence if it doesn't exist
  \seq_if_exist:cF { l_bcp_data_#1_seq } { \seq_new:c { l_bcp_data_#1_seq } }
  % populate the sequence
  \seq_set_split:cnn { l_bcp_data_#1_seq } { } { #2 }
 }
\cs_generate_variant:Nn \seq_set_split:Nnn { c }

\cs_new:Npn \bcp_get_data:nn #1 #2
 {
  % retrieve the requested item
  \seq_item:cn { l_bcp_data_#2_seq } { #1 }
 }
\ExplSyntaxOff

\begin{document}

\storedata{mydata}{{one}{two}{three}}

\getdata[1]{mydata}

\getdata[2]{mydata}

\getdata[3]{mydata}

\end{document}

在此处输入图片描述

你甚至可以使用\getdata[1+1+1]{mydata}或使用计数器,比如说

\newcounter{acounter} % in the preamble

\setcounter{acounter}{2} % somewhere in the document
\getdata[\value{acounter}]{mydata}

的第二个参数\storedata应该用括号括起来,因为否则就不可能知道它在哪里结束。

扩展实现expl3

一个简单的扩展,还允许附加到列表,计算项目数并删除最后一项(可选择将其存储在宏中)。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\storedata}{mm}
  {
   \bcp_store_data:nn { #1 } { #2 }
  }

\NewDocumentCommand{\appenddata}{mm}
 {
  \bcp_append_data:nn { #1 } { #2 }
 }

\NewExpandableDocumentCommand{\getdata}{O{1}m}
 {
  \bcp_get_data:nn { #1 } { #2 }
 }

\NewExpandableDocumentCommand{\getlength}{m}
 {
  \seq_count:c { l_bcp_data_#1_seq }
 }

\NewDocumentCommand{\removelast}{om}
 {
  \IfNoValueTF { #1 }
   {
    \bcp_remove_last:Nn \l_tmpa_tl { #2 }
   }
   {
    \bcp_remove_last:Nn #1 { #2 }
   }
 }

\cs_new_protected:Npn \bcp_store_data:nn #1 #2
 {
  % create the sequence if it doesn't exist or clear it if it exists
  \seq_clear_new:c { l_bcp_data_#1_seq }
  % append the items
  \__bcp_append_data:nn { #1 } { #2 }
 }

\cs_new_protected:Npn \bcp_append_data:nn #1 #2
 {
  % create the sequence if it doesn't exist, do nothing if it exists
  \seq_if_exist:cF { l_bcp_data_#1_seq }
   { \seq_new:c { l_bcp_data_#1_seq } }
  % append the items
  \__bcp_append_data:nn { #1 } { #2 }
 }

\cs_new_protected:Npn \__bcp_append_data:nn #1 #2
 {
  % append items one at a time
  \tl_map_inline:nn { #2 }
   {
    \seq_put_right:cn { l_bcp_data_#1_seq } { ##1 }
   }
 }

\cs_new:Npn \bcp_get_data:nn #1 #2
 {
  % retrieve the requested item
  \seq_item:cn { l_bcp_data_#2_seq } { #1 }
 }

\cs_new_protected:Nn \bcp_remove_last:Nn
 {
  \seq_pop_right:cN { l_bcp_data_#2_seq } #1
 }

\ExplSyntaxOff

\begin{document}

\storedata{mydata}{{one}{two}}

Length is: \getlength{mydata}

\appenddata{mydata}{{three}{four}}

Length is: \getlength{mydata}

\getdata[1]{mydata}

\getdata[2]{mydata}

\getdata[3]{mydata}

\getdata[4]{mydata}

\removelast{mydata}

Length is: \getlength{mydata}

\removelast[\test]{mydata}

\test % should be 'three'

\end{document}

在此处输入图片描述

对于这两种解决方案,也可以说

\getdata[-1]{mydata}

检索最后一个项目;检索-2倒数第二个项目,依此类推。因此

\getdata[-1]{mydata}\par
\getdata[-2]{mydata}\par
\getdata[-3]{mydata}\par
\getdata[-4]{mydata}

将打印


三二一

一个更简单(但不太灵活)的解决方案expl3

当然,还有一种不太灵活的解决方案,我不推荐,因为序列中的数据比标记列表中的数据更易于检索。但是,这是所有可扩展解决方案中最短的\getdata

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\storedata}{mm}{\tl_set:Nn#1{#2}}
\DeclareExpandableDocumentCommand{\getdata}{O{1}m}{\tl_item:Nn#2{#1}}
\ExplSyntaxOff

\begin{document}

\storedata\mydata{{one}{two}{three}}

\getdata[1]\mydata

\getdata[2]\mydata

\getdata[3]\mydata

\getdata[-1]\mydata

\getdata[-2]\mydata

\getdata[-3]\mydata

\end{document}

“经典”的实现

假设您只有三件物品需要存储。

\documentclass{article}

\newcommand\storedata[4]{\def#1{{#2}{#3}{#4}}}

\makeatletter
\providecommand\@firstofthree[3]{#1}
\providecommand\@secondofthree[3]{#2}
\providecommand\@thirdofthree[3]{#3}

\def\getdata[#1]#2{%
  \ifcase#1 \ERROR\or
  \expandafter\@firstofthree#2\or
  \expandafter\@secondofthree#2\or
  \expandafter\@thirdofthree#2\else
  \ERROR\fi}
\makeatother

\begin{document}

\storedata\mydata{one}{two}{three}

\getdata[1]\mydata\par   % returns one
\getdata[2]\mydata\par   % returns two
\getdata[3]\mydata\par   % returns three

\end{document}

\getdata是可扩展的,这不是\ifthenelse基于的方法。

答案3

以下示例使用\ltx@CarNumthfrom 包ltxcmds从组标记列表中选择一个元素。该列表可以存储为简单的宏:

\documentclass{article}

\usepackage{ltxcmds}
\makeatletter
\newcommand*{\getdata}[2]{%
  \expandafter\ltx@CarNumth\expandafter{%
    \the\numexpr(#1)\expandafter
  }#2\@nil
}
\makeatother

\newcommand*{\mydata}{{one}{two}{three}}

\newcommand*{\mylongdata}{
  {one} {two} {three} {four} {five} {six} {seven} {eight} {nine} {ten}
  {eleven} {twelve} {thirteen} {fourteen} {fifteen} {sixteen}
  {seventeen} {eighteen} {nineteen} {twenty} {twenty-one} {twenty-two}
  {twenty-three} {twenty-four}
}

\begin{document}
\begin{itemize}
\item \getdata{1}\mydata
\item \getdata{2}\mydata
\item \getdata{3}\mydata
\item \getdata{24}\mylongdata
\end{itemize}
\end{document}

结果

评论:

  • 解 ( \getdata) 完全可以展开。
  • 数字参数还可以包含类似 的表达式1+2。 (内部使用 e-TeX 的\numexpr表达式。)

答案4

该解决方案扩展了回答擦拭:

  • 如果选择器编号超出范围或者数据未知,则会添加错误消息。

  • 项目数量存储在位置 0。

  • \getdata也接受否定论点,从列表末尾开始计算。

  • 和 wipet 的解决方案一样,项目数量仅限于 TeX 的最大整数值 (2 31 - 1 = 2147483647)。可以使用包来限制此限制bigintcalc,但在实践中,哈希表大小等内存限制很可能在此之前就已达到。

  • 的数字参数\getdata也接受表达式,因为该参数是通过 e-TeX 的传递的\numexpr

  • 解决方案(\getdata)除了错误情况外都是可扩展的。错误消息之类的消息是不可扩展的。

  • 只使用不带反斜杠的名称来代替命令序列。

完整示例:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[variablett]{lmodern}

\makeatletter
\newcommand*{\storedata}[2]{%
  \count@=0 %
  \@tfor\@tmp:=#2\do{%
    \advance\count@\@ne
    \expandafter\let\csname data:\the\count@:#1\endcsname\@tmp
  }%
  \expandafter\edef\csname data:0:#1\endcsname{\the\count@}%
}
\newcommand*{\getdata}[2]{%
  \@ifundefined{data:0:#2}{%
    \@latex@error{Undefined data `#2'}\@ehc
  }{%
    \expandafter\@getdata\expandafter{%
      \the\numexpr
        \ifnum\numexpr(#1)<\z@
          \@nameuse{data:0:#2}+1+%
        \fi
        (#1)%
      \relax
    }{#2}{#1}%
  }%
}
\newcommand*{\@getdata}[3]{%
  \ifnum#1<\z@
    \@getdata@error{\the\numexpr(#3)\relax}{#2}%
  \else
    \ifnum#1>\@nameuse{data:0:#2} %
      \@getdata@error{#1}{#2}%
    \else
      \@nameuse{data:#1:#2}%
    \fi
  \fi
}
\newcommand*{\@getdata@error}[2]{%
  \@latex@error{%
    Wrong data selector #1 for `#2',\MessageBreak
    which only contains \@nameuse{data:0:#2} item(s)%
  }\@ehc
}
\makeatother

\storedata{mydata}{{one}{two}{three}}
\storedata{mylongdata}{
  {one} {two} {three} {four} {five} {six} {seven} {eight} {nine} {ten}
  {eleven} {twelve} {thirteen} {fourteen} {fifteen} {sixteen}
  {seventeen} {eighteen} {nineteen} {twenty} {twenty-one} {twenty-two}
  {twenty-three} {twenty-four}
}

\begin{document}
\newcommand*{\test}[2]{%
  \ttfamily #2[#1]: &
  \getdata{#1}{#2}
  \tabularnewline
}
\begin{tabular}{@{}l@{ }l@{}}
  \test{0}{mydata}
  \test{1}{mydata}
  \test{2}{mydata}
  \test{3}{mydata}
  \test{-1}{mydata}
  \test{-2}{mydata}
  \test{-3}{mydata}
  \test{-4}{mydata}
  \hline
  \test{0}{mylongdata}
  \test{1}{mylongdata}
  \test{24}{mylongdata}
  \test{-10}{mylongdata}
\end{tabular}
\end{document}

结果

相关内容