在没有任何内置循环宏的情况下,我们可以仅使用递归宏来获得输出a-b-c-d-e
吗?
\def\aaa #1{#1-\aaa}
\aaa abcde
% expected result : a-b-c-d-e
\bye
编辑:
可能最好的调用方式\aaa
应该是\aaa{abcde}
终止递归的条件仍然是一个悬而未决的问题。
答案1
如果预期的参数\aaa
是一串字符,那么
\catcode`@=11
\def\aaa#1{\a@a#1\@nil}
\def\a@a#1{\ifx#1\@nil\expandafter\empty\else#1-\expandafter\a@a\fi}
\catcode`@=12
\aaa{abcde}
\bye
可以。如果参数更复杂,带有括号组或宏,则更困难。
这是完全可扩展的,并且\edef\foo{\aaa{abcde}}\show\foo
会输出
> \foo=macro:
->a-b-c-d-e-.
您的第一次尝试无法成功,因为没有终止条件。
-
可以按如下方式添加对支撑组的支持并删除尾随部分:
\catcode`@=11
\def\q@stop{\q@stop}
\def\q@nil{\q@nil}
\long\def\@gobble#1{}
\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}
\def\aaa#1{\aa@#1\q@stop\q@nil}
\def\aa@#1#2{%
\ifx\q@stop#2%
#1\expandafter\@firstoftwo
\else
#1-\expandafter\@secondoftwo
\fi
{\@gobble}%
{\aa@{#2}}%
}
\catcode`@=12
\aaa{abcde}
\aaa{a{bc}de}
\bye
注意\aa@
有两个参数,所以我们可以吸收一个组;测试是在第二个参数上进行的,如果 ,则停止递归\q@stop
。我把 的情况留作练习\aaa{}
。
答案2
以下示例使用空格作为终止符,并支持使用括号(包括空括号)对字母进行分组。该解决方案可扩展:
\def\aaa#1 {%
\aaaX#1\empty\empty
}
\def\aaaX#1#2{%
#1%
\ifx\empty#2\longempty
\expandafter\gobble
\else
-%
\expandafter\aaaX
\fi
{#2}%
}
\long\def\longempty{}
\long\def\gobble#1{}
%%% test %%%
\def\test#1{%
\immediate\write16{[\noexpand\aaa #1 ] => [\aaa #1 ]}%
}
\test{abcde}
\test{a}
\test{}
\test{aaa}
\test{x{}{yy}}
\bye
结果:
[\aaa abcde ] => [a-b-c-d-e]
[\aaa a ] => [a]
[\aaa ] => []
[\aaa aaa ] => [a-a-a]
[\aaa x{}{yy} ] => [x--yy]
答案3
@egreg 的回答有一个错误:问题作者不希望最后一个字母后面有连字符。有一个完整的可扩展解决方案可以解决这个问题:
\def\aaa #1{\ifx\end#1\end\else \aaaB #1\end\fi}
\def\aaaB #1#2\end{#1\aaaC#2\end}
\def\aaaC #1{\ifx\end#1\empty\else -#1\expandafter\aaaC \fi}
\aaa {abcde}
\bye
编辑:我添加了一个\ifx
以接受空参数。还支持使用括号(包括空括号)对字母进行分组。