灵感来自访问列表特定成员的宏。
我想要一个宏来实现 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{%
}