用于检查元素是否为列表成员并访问关联列表的宏

用于检查元素是否为列表成员并访问关联列表的宏

灵感来自访问列表特定成员的宏

我想要一个宏来实现 lisp 函数(member item list)和。第一个宏检查某个项目是否包含在列表中,第二个宏使用关联列表中(assoc id alist)的给定值访问该项目。id

该宏应该能够获取文字列表或宏。

用法应该是这样的:

\ifassoc{42}{1,2,3}{if-found}{if-not-found}
\ifassoc{42}{\somelist}{if-found}{if-not-found}
\ifassoc{b}{a/1,b/2,c/3}{\assocresult equals 2}{if-not-found}
\ifassoc{b}{\otherlist}{\assocresult can be used here}{if-not-found}

答案1

该宏满足规格:

\newcommand{\ifassoc}[4]{%
  \edef\dotheloop{%
    \noexpand\foreach \noexpand\a/\noexpand\b in {#2} {%
      \noexpand\IfEq{\noexpand\a}{#1}{%
        \noexpand\gdef\noexpand\memberresult{true}%
        \noexpand\xdef\noexpand\assocresult{\noexpand\b}%
        \noexpand\breakforeach%
      }{}%
    }%
  }%
  \xdef\assocresult{}%
  \xdef\memberresult{false}
  \dotheloop%
  \IfEq{\memberresult}{true}{#3}{#4}%
}%

梅威瑟:

\documentclass{article}
\usepackage{tikz}
\usepackage{xstring}
\begin{document}

\newcommand{\ifassoc}[4]{%
  \edef\dotheloop{%
    \noexpand\foreach \noexpand\a/\noexpand\b in {#2} {%
      \noexpand\IfEq{\noexpand\a}{#1}{%
        \noexpand\gdef\noexpand\memberresult{true}%
        \noexpand\xdef\noexpand\assocresult{\noexpand\b}%
        \noexpand\breakforeach%
      }{}%
    }%
  }%
  \xdef\assocresult{}%
  \xdef\memberresult{false}
  \dotheloop%
  \IfEq{\memberresult}{true}{#3}{#4}%
}%

% Simple list of important numbers, given inline.
\ifassoc{2.72}{42,3.14}{
  This text will not be displayed.
}{
  Whoops, we forgot to put $e$ !
}

\ifassoc{2.72}{42,3.14,2.72}{
  $e$ is an important number. \texttt{\textbackslash assocresult=\assocresult}.
}{
  This text will not be displayed.
}

% Associative list of associative lists, given as a macro.
\def\collections{
  Colors/{1/red,2/green,3/blue},
  Foo/{1/foo,2/bar,3/baz,4/quux},
  Letters/{1/a,2/b,3/c,4/d,5/e}%
}
\ifassoc{Foo}{\collections}{
  % We can run \foreach on the result of \ifassoc.
  Found id \texttt{Foo}, associated with the pairs
  \foreach \i/\j in \assocresult {\i\ and \j, }%
  and that's all.
  % We can run \ifassoc again on the result of \ifassoc.
  \ifassoc{4}{\assocresult}{The element with id \texttt{4} is \assocresult.}{This text will not be displayed.}
  \ifassoc{5}{\assocresult}{This text will not be displayed.}{There is no element with id \texttt{5}.}
}{
  This text will not be displayed
}

\ifassoc{Digits}{\collections}{
  This text will not be displayed.
}{
  There is no collection with id \texttt{Digits}.
}

\end{document}

输出:

上述代码的输出

答案2

像往常一样,expl3有简单的工具来实现这一点。我希望代码是不言自明的(好吧,评论...)

\documentclass{article}
\usepackage{xparse,expl3}

\ExplSyntaxOn
% variables:
\bool_new:N \l_georges_assoc_found_bool
\tl_new:N   \l_georges_assoc_item_tl
\tl_new:N   \l_georges_assoc_value_tl
\tl_new:N   \assocresult

% the main function:
\cs_new_protected:Npn \georges_ifassoc:nnTF #1#2#3#4
  {
    \bool_set_false:N \l_georges_assoc_found_bool
    \tl_clear:N \l_georges_assoc_value_tl
    % go through the comma list:
    \clist_map_inline:nn { #2 }
      {
        % separate item and (possible) value:
        \georges_separate_item_value:w ##1 // \q_stop
        % if we found the item stop the loop:
        \tl_if_eq:nVT { #1 } \l_georges_assoc_item_tl
          {
            \bool_set_true:N \l_georges_assoc_found_bool
            \clist_map_break:
          }
      }
    % make the value accessible:
    \tl_set_eq:NN \assocresult \l_georges_assoc_value_tl
    \bool_if:NTF \l_georges_assoc_found_bool { #3 } { #4 }
  }
\cs_generate_variant:Nn \tl_if_eq:nnT { nV }
\cs_generate_variant:Nn \georges_ifassoc:nnTF { no }

% separate item and value:    
\cs_new:Npn \georges_separate_item_value:w #1/#2/#3 \q_stop
  {
    % store item and remove trailing and leading space:
    \tl_set:Nx \l_georges_assoc_item_tl { \tl_trim_spaces:n { #1 } }
    % #3 = / if value given:
    \tl_if_eq:nnT { #3 } { / }
      { \tl_set:Nn \l_georges_assoc_value_tl { #2 } }
  }

% user command:
\NewDocumentCommand \ifassoc { mmmm }
  { \georges_ifassoc:noTF { #1 } { #2 } { #3 } { #4 } }
\ExplSyntaxOff

\begin{document}

\def\somelist{1,2,3,42}
\def\otherlist{a/1,b/2,c/3}

\ifassoc{42}{1,2,3}{if-found}{if-not-found}

\ifassoc{42}{\somelist}{if-found}{if-not-found}

\ifassoc{b}{a/1,b/2,c/3}{\assocresult\ equals 2}{if-not-found}

\ifassoc{b}{\otherlist}{\assocresult\ can be used here}{if-not-found}

\ifassoc{42}{ 42 /ab , 43/cd }{\assocresult}{F}

\ifassoc{ab}{ ab /42 , cd/43 }{\assocresult}{F}

\end{document}

在此处输入图片描述

答案3

我希望我现在能陈述这个事实,并在稍后给出完整的解决方案。为了说明我上面的观点 (B),请尝试:

\makeatletter
\newcount\cnta
\cnta\z@
\foreach \p/\q in {A,B}{%
  \global\advance\cnta\@ne
  \expandafter\xdef\csname result@\romannumeral\cnta\endcsname{\q}%
}
\edef\result{\result@i,\result@ii}
\show\result
\makeatother

将会优先考虑如下的通用方案,但我猜想已经有一个提供此 DB 功能的包。

\def\collections{
  1/colors:        1/red, 2/green, 3/blue;
  2/tech makers:   1/Toyota, 2/Apple, 3/Samsung, 4/Motorola, 5/Honda;
  3/IT firms:      1/Apple, 2/Microsoft, 3/Google, 4/Oracle;
  4/automakers:    1/Toyota, 2/Ford, 3/Mercedes, 4/GM, 5/Honda;
  5/units:         1/pt, 2/ex, 3/em, 4/cc, 5/in;
  6/sports:        1/basketball, 2/tennis, 3/football, 4/soccer;
  7/energy:        1/gas, 2/oil, 3/coal, 4/solar, 5/nuclear;
}

% Eg, \addrow{Continents: Africa, Asia, Europe, N.America, S.America, Ausie?}
\def\addrow#1{%

}
% Eg, \removerow{IT firms}
\def\removerow#1{%

}
% Eg, \addcol{units}{mm}
\def\addcol#1#2{%

}
% Eg, \removecol{sports}{tennis}
\def\removecol#1#2{%

}

% 1. What is the association (row) of automakers?
% 2. What is the association (column) of GM in automakers?

% Eg, \rowof{automakers}
\def\rowof#1{%

}
% Eg, \rowandcolof{automakers}{GM}
\def\rowandcolof#1#2{%

} 

相关内容