我想通过删除最后一段来解析单词(“1. Book1 p3”变为“1. Book1”或“Book2 p4”变为“Book2”)。我使用函数实现了这一点\FirstPart
。现在,我想\FirstPart
在函数中使用返回\foo
参数来进行字符串比较。如果输入参数符合 ifthenelse 测试,它应该返回相应的键(在示例中,如果输入是“1. Book p.2”,则返回应该是“01Category”)。所以我测试了\foo{\FirstPart{1. Book p.2}}
,但是我总是得到 FALSE 作为返回值。我认为存在类型不匹配,但是我不知道如何解决这个问题。最后我想使用\foo
指定(makeindex 包)的排序顺序\index
(自定义环境中的一个条目看起来像:\index{\foo{\FirstPart{#1}}@\FirstPart{#1}!#1}
。有人能帮帮我吗?
\documentclass{article}
\makeatletter
\newcommand\FirstPart[1]{#1}%
\newcommand\firstwords{}%
\def\firstwords#1 #2 #3\@nil{\if\relax#3\relax
#1\relax
\else
\foo{#1 #2\relax}
\fi}
\makeatother
\usepackage{ifthen}
\newcommand{\foo}[1]{%
\ifthenelse{\equal{#1}{1. Book}}
{01Category}
{FALSE}}
\begin{document}
\foo{\FirstPart{1. Book p.2}}\\
\foo{1. Book}
\end{document}
答案1
不太清楚你想要实现什么。也许是这样的?
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\firstwordscase}{mmm}
{
\ise_firstwordscase:enn { \text_expand:n { #1 } } { #2 } { #3 }
}
\cs_new:Nn \ise_firstwordscase:nnn
{
\__ise_firstwordscase:nnw { #2 } { #3 } #1 \q_stop
}
\cs_generate_variant:Nn \ise_firstwordscase:nnn { e }
\cs_new:Npn \__ise_firstwordscase:nnw #1 #2 #3 ~ #4 ~ #5 \q_stop
{
\str_case:nnF { #3~#4 } { #1 } { #2 }
}
\ExplSyntaxOff
\newcommand{\test}{1. Book2 p3} % just for testing
\begin{document}
\firstwordscase{1. Book1 p3}{
{1. Book1}{01Category}
{1. Book2}{XYZ}
}{FALSE}
\firstwordscase{1. Book2 p3}{
{1. Book1}{01Category}
{1. Book2}{XYZ}
}{FALSE}
\firstwordscase{\test}{
{1. Book1}{01Category}
{1. Book2}{XYZ}
}{FALSE}
\firstwordscase{2. Book1 p1}{
{1. Book1}{01Category}
{1. Book2}{XYZ}
}{FALSE}
\end{document}
你可以有任意多个大小写切换。
答案2
为了多样化,这里有一个基于 LuaLaTeX 的解决方案。它由两个 LaTeX 实用程序宏组成,称为\FirstPart
和\foo
,它们\directlua
分别调用 Lua 函数,你猜对了,FirstPart
和foo
。观察因为\directlua
是可扩展的,\foo{\FirstPart{1. Book p.2}}
并\foo{1. Book}
产生相同的输出。
\documentclass{article}
\usepackage{luacode}
%% Lua-side code:
\begin{luacode}
function FirstPart ( s )
n = s:find ( "%s+%S+$" )
tex.sprint ( s:sub ( 1 , n - 1 ) )
end
function foo ( s )
if s == "1. Book" then
tex.sprint ( "01Category" )
else
tex.sprint ( "FALSE" )
end
end
\end{luacode}
%% LaTeX-side code:
\newcommand\FirstPart[1]{\directlua{FirstPart("#1")}}
\newcommand\foo[1]{\directlua{foo("#1")}}
\begin{document}
\obeylines % just for this example
\FirstPart{1. Book1 p3}
\FirstPart{Book2 p4}
\foo{\FirstPart{1. Book p.2}}
\foo{1. Book}
\foo{\FirstPart{Book2 p4}}
\end{document}
答案3
\foo
可能仅通过扩展方法测试其参数(扩展后)在附加空格后是否足够的命令?1.⟨space⟩Book⟨space⟩
\makeatletter
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange,
%% \UD@stopromannumeral, \UD@CheckWhetherNull,
%% \UD@CheckWhetherLeadingTokens, \UD@internaltokencheckdefiner
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific
%% token-sequence that does not contain explicit character tokens of
%% category 1 or 2 or 6:
%%.............................................................................
%% \UD@CheckWhetherLeadingTokens{<argument which is to be checked>}%
%% {<a <token sequence> without explicit
%% character tokens of category 1 or 2
%% or 6>}%
%% {<internal token-check-macro>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked> has
%% <token sequence> as leading tokens>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked>
%% does not have <token sequence> as
%% leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[3]{%
\romannumeral\UD@CheckWhetherNull{#1}%
{\expandafter\UD@stopromannumeral\UD@secondoftwo}%
{%
% Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
% and thus do not disturb when the test is carried out within \halign/\valign:
\expandafter\UD@firstoftwo\expandafter{%
\expandafter\expandafter\expandafter\UD@stopromannumeral
\romannumeral
\expandafter\UD@secondoftwo\string{\expandafter\UD@@CheckWhetherLeadingTokens#3{{}}#1#2}{}}{}%
}%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\expandafter\expandafter\expandafter\UD@stopromannumeral
\expandafter\expandafter\expandafter}%
\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%% {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything
%% until reaching <token sequence>-sequence and spit that out
%% nested in braces.
%%-----------------------------------------------------------------------------
\newcommand\UD@internaltokencheckdefiner[2]{%
\@ifdefinable#1{\long\def#1##1#2{{##1}}}%
}%
\makeatother
\documentclass{article}
\makeatletter
\UD@internaltokencheckdefiner{\GobbleTillFirstBookPhrase}{1. Book }%
\newcommand\foo[1]{%
\expanded{\noexpand\UD@CheckWhetherLeadingTokens{#1 }}%
{1. Book }%
{\GobbleTillFirstBookPhrase}%
{01Category}%
{FALSE}%
}%
\newcommand\FirstPart[1]{#1}%
\makeatother
\begin{document}
\noindent
Test 1: \foo{\FirstPart{1. Book p.2}}\\
Test 2: \foo{\FirstPart{1. Book p.2 some } more text}\\
Test 3: \foo{1. Book}\\
Test 4: \foo{2. Book p. 2}\\
Test 5: \foo{1. Bookbinding}
\end{document}
我考虑详细阐述一个循环,该循环从参数的右侧连续删除以空格分隔的组件,并迭代执行\ifthenelse
-comparison,直到字符串与短语匹配1. Book
或不包含空格。
但您提到您希望将其与命令一起使用\index
。在许多地方,命令的参数\index
需要完全可扩展,而对于如果那么的\ifthenelse
命令。
答案4
以下内容受到egreg的回答的启发:
\firstwordscase
使用其第二个参数执行“文本扩展”,去掉前导和尾随空格标记,然后提取最多与第一个参数所表示的一样多的空格分隔单词。然后使用第三个和第四个参数所表示的结果执行字符串大小写转换。第二个参数中单词之间的多个空格标记合并为一个空格标记。括号组的内容被视为一个单词,其中周围的括号组不会被去掉,空格标记等也不会受到影响。
\ExplSyntaxOn
\errorcontextlines=100000
\cs_new:Npn \UD_ExtractFirstSpaceDelimitedArg:n #1#2 {
\cs_new:Npn \UD_GobbleKnownFirstSpace:w #2 {}
\cs_new:Npn \UD_GobbleFirstSpaceDelimitedArg:w ##1#2 {}
\cs_new:Npn \UD_RemoveSecondSpaceDelimitedArg:ww ##1#2##2#2#1{{##1}#2}
\cs_new:Npn \UD_RemoveBracesFromSpaceDelimitedArg:w ##1#2{\use_i:nn ##1}
%%-----------------------------------------------------------------------------
%% Loop for extracting the first space-delimited argument from list of
%% space-separated components - the result is returned wrapped in curly braces
%%.............................................................................
\cs_gset:Npn \UD_ExtractFirstSpaceDelimitedArg:n ##1 {
\exp:w \UD_ExtractFirstSpaceDelimitedLoop:n{{}{}##1#2#2#1}
}
\cs_new:Npn\UD_ExtractFirstSpaceDelimitedLoop:n ##1 {
\tl_if_empty:oTF {\UD_GobbleFirstSpaceDelimitedArg:w ##1}
{ \exp_args:Nno \use:n {\exp_args:No\exp_end:}{\UD_RemoveBracesFromSpaceDelimitedArg:w ##1} }
{ \exp_args:No\UD_ExtractFirstSpaceDelimitedLoop:n{\UD_RemoveSecondSpaceDelimitedArg:ww ##1} }
}
%%-----------------------------------------------------------------------------
%% Removal of the first space-delimited argument from list of space-separated
%% components - the resulting list is returned wrapped in curly braces
%%.............................................................................
\cs_new:Npn \UD_RemoveFirstSpaceDelimitedArg:n ##1 {
\exp:w \tl_if_empty:oTF {\UD_GobbleFirstSpaceDelimitedArg:w ##1#2}
{\exp_end:{}}{\exp_args:No\exp_end:{\UD_GobbleFirstSpaceDelimitedArg:w ##1}}
}
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with an explicit space-token
%%.............................................................................
%% \CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked> does have a
%% leading explicit space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked> does not have a
%% a leading explicit space-token>}%
\cs_new:Npn \CheckWhetherLeadingExplicitSpace ##1 {
\exp:w \tl_if_empty:nTF {##1}
{\exp_after:wN \exp_end: \use_ii:nn}
{ % Let's nest things into \use_i:nn{...}{} to make sure they are nested in braces
% and thus do not disturb when the test is carried out within \halign/\valign:
\exp_after:wN \use_i:nn \exp_after:wN {%
\exp_after:wN \exp_after:wN \exp_after:wN \exp_end:
\exp:w \exp_after:wN \use_ii:nn
\token_to_str:N { \UD_CheckWhetherLeadingExplicitSpace:w#1##1#2 }{}
}{}
}
}
\cs_new:Npn \UD_CheckWhetherLeadingExplicitSpace:w ##1#2 {
\tl_if_empty:oTF {\use_i:nn{}##1}
{\use_ii_i:nn{\use_i:nn}}{\use_ii_i:nn{\use_ii:nn}}
{\exp_after:wN\exp_after:wN\exp_after:wN\exp_end:
\exp_after:wN\exp_after:wN\exp_after:wN}
\exp_after:wN\use_ii:nn\exp_after:wN{\token_to_str:N}
}%
%%-----------------------------------------------------------------------------
}
\exp_args:Nno\use:n{\exp_args:No\UD_ExtractFirstSpaceDelimitedArg:n}
{\exp_after:wN \exp_after:wN \tex_ifnum:D 0=0\tex_fi:D }{~}
%%%
%%-----------------------------------------------------------------------------
%% Loop for trimming all leading explicit space tokens from argument
%%
%% \TrimAllLeadingSpaces{<argument>}{<tokens to prepend>}
%%
%% delivers <tokens to prepend>{<argument>} , but with all leading spaces of
%% <argument> removed.
%%.............................................................................
\cs_new:Npn \TrimAllLeadingSpaces #1 {
\CheckWhetherLeadingExplicitSpace{#1}{
\exp_args:No \TrimAllLeadingSpaces {\UD_GobbleKnownFirstSpace:w #1}
}{ \use_ii_i:nn{{#1}} }
}
%----------------------------------------------------------------------------------------
%% Loop for trimming all trailing explicit space tokens from argument
%%
%% \TrimAllTrailingSpaces{<argument>}{<tokens to prepend>}
%%
%% delivers <tokens to prepend>{<argument>} , but with all trailing spaces
%% of <argument> removed.
%%
%% Algorithm:
%% Initate a loop with a tail-recursive macro with 3 arguments:
%% 1. <Space from previous iteration>. Initially empty.
%% 2. <Argument without trailing spaces gathered so far>. Initially empty.
%% 3. <Remaining argument to process>. Initially <Arg><space>.
%%
%% If <Remaining argument to process> is not blank:
%% Call \trimalltrailingspaces again, with
%% <Argument without trailing spaces gathered so far>=
%% <Argument without trailing spaces gathered so far>+<Space from previous iteration>+
%% <Extraction of 1st space-delimited Arg from <Remaining argument to process> >;
%% <Remaining argument to process>=<Remaining argument to process> with 1st space-delimited arg removed;
%% <Space from previous iteration>=<explicit space token>;
%%
%% If <Remaining argument to process> is blank:
%% Pass <Argument without trailing spaces gathered so far> as argument to
%% <tokens to prepend> .
%%.............................................................................
\cs_new:Npn \TrimAllTrailingSpaces #1 {\exp:w\UD_TrimAllTrailingSpacesLoop:www{}{}{#1~}}
\cs_new:Npn \UD_TrimAllTrailingSpacesLoop:www #1#2#3 {
\tl_if_blank:nTF {#3}{\exp_after:wN \exp_end: \use_ii_i:nn{{#2}}}{
\exp_after:wN \exp_after:wN \exp_after:wN \use_ii_i:nn
\exp_after:wN \exp_after:wN \exp_after:wN {
\UD_RemoveFirstSpaceDelimitedArg:n {#3}
}{
\exp_after:wN \use_ii_i:nn \exp_after:wN { \exp_after:wN {
\exp:w \exp_after:wN \exp_after:wN \exp_after:wN \use_ii_i:nn
\UD_ExtractFirstSpaceDelimitedArg:n {#3}{\exp_end: #2#1}
}}{\UD_TrimAllTrailingSpacesLoop:www{~}}
}
}
}
%%-----------------------------------------------------------------------------
\cs_new:Npn \UD_ExtractWords:nn #1#2 {
\exp:w \TrimAllLeadingSpaces { #2 }
{ \TrimAllTrailingSpaces }
{ \UD_ExtractWordsloop:nnnn {}{} }
{ #1 }
}
\cs_new:Npn \UD_ExtractWordsloop:nnnn #1#2#3#4 {
% % #1 stuff to prepend , #2 gathered so far, #3 remaining list, #4 number
\int_compare:nNnTF {(#4)} > {(0)}
{
\tl_if_empty:nTF {#3} { \exp_end: #2 }{
\CheckWhetherLeadingExplicitSpace{#3}{
\exp_after:wN \use_ii_i:nn \exp_after:wN { \exp_after:wN { \UD_GobbleKnownFirstSpace:w #3 } }
{ \UD_ExtractWordsloop:nnnn {#1}{#2}}{#4}
}{
\exp_after:wN \exp_after:wN \exp_after:wN \use_ii_i:nn
\exp_after:wN \exp_after:wN \exp_after:wN {
\exp_after:wN \exp_after:wN \exp_after:wN {\int_eval:n {((#4)-1)}}
}{
\exp_args:Nno \use:n { \exp_args:No \use_ii_i:nn } {
\UD_RemoveFirstSpaceDelimitedArg:n {#3}
}{
\exp_args:No \use_ii_i:nn { \exp_after:wN {
\exp:w
\exp_after:wN \exp_after:wN \exp_after:wN
\use_ii_i:nn \UD_ExtractFirstSpaceDelimitedArg:n {#3}{\exp_end: #2#1}
}}{
\UD_ExtractWordsloop:nnnn {~}
}
}
}
}
}
}
{\exp_end: #2}
}
\cs_new:Npn \UD_firstwordscase:nn #1#2 {
\exp_args:Nno \use:n { \exp_args:No\str_case:nnF } { \UD_ExtractWords:nn {#1}{#2} }
}
\cs_generate_variant:Nn \UD_firstwordscase:nn { ne }
\cs_new:Npn \firstwordscase #1#2 { \UD_firstwordscase:ne {#1}{\text_expand:n {#2}} }
\ExplSyntaxOff
\documentclass{article}
\newcommand{\test}{1. Book2 p3} % just for testing
\begin{document}
\firstwordscase{2}{1. Book1 p3}{
{1. Book1}{The second argument belongs to 1. Book1.}
{1. Book2}{The second argument belongs to 1. Book2.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{2}{1. Book2 p3}{
{1. Book1}{The second argument belongs to 1. Book1.}
{1. Book2}{The second argument belongs to 1. Book2.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{2}{\test}{
{1. Book1}{The second argument belongs to 1. Book1.}
{1. Book2}{The second argument belongs to 1. Book2.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{2}{2. Book1 p1}{
{1. Book1}{The second argument belongs to 1. Book1.}
{1. Book2}{The second argument belongs to 1. Book2.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{2}{2. Book1 p1}{
{2. Book1}{The second argument belongs to 2. Book1.}
{1. Book2}{The second argument belongs to 1. Book2.}
}{The second argument doesn't belong to any of the further.}
\def\spaceimport#1{\firstwordscase{2}{2.#1#1#1#1Book1#1#1#1#1p1}}
\spaceimport{ }{
{2. Book1}{The second argument belongs to 2. Book1.}
{1. Book2}{The second argument belongs to 1. Book2.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{1}{1 A a i I - second argument }{
{1}{The second argument belongs to 1.}
{2}{The second argument belongs to 2.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{1}{2 A a i I - second argument }{
{1}{The second argument belongs to 1.}
{2}{The second argument belongs to 2.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{2}{1 A a i I - second argument }{
{1 A}{The second argument belongs to 1 A.}
{1 B}{The second argument belongs to 1 B.}
{2 A}{The second argument belongs to 2 A.}
{2 B}{The second argument belongs to 2 B.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{2}{2 B a i I - second argument }{
{1 A}{The second argument belongs to 1 A.}
{1 B}{The second argument belongs to 1 B.}
{2 A}{The second argument belongs to 2 A.}
{2 B}{The second argument belongs to 2 B.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{3}{1 A a i I - second argument }{
{1 A a}{The second argument belongs to 1 A a.}
{1 B a}{The second argument belongs to 1 B a.}
{2 A a}{The second argument belongs to 2 A a.}
{2 B a}{The second argument belongs to 2 B a.}
{1 A b}{The second argument belongs to 1 A b.}
{1 B b}{The second argument belongs to 1 B b.}
{2 A b}{The second argument belongs to 2 A b.}
{2 B b}{The second argument belongs to 2 B b.}
}{The second argument doesn't belong to any of the further.}
\firstwordscase{3}{ 2 B a i I - second argument }{
{1 A a}{The second argument belongs to 1 A a.}
{1 B a}{The second argument belongs to 1 B a.}
{2 A a}{The second argument belongs to 2 A a.}
{2 B a}{The second argument belongs to 2 B a.}
{1 A b}{The second argument belongs to 1 A b.}
{1 B b}{The second argument belongs to 1 B b.}
{2 A b}{The second argument belongs to 2 A b.}
{2 B b}{The second argument belongs to 2 B b.}
}{The second argument doesn't belong to to any of the further.}
\end{document}