マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

文字列の置換

再び TeX 言語の練習問題を出してみる。

以下の要件を満たすマクロ \replaceWordAll を実装せよ。

  • \replaceWordAll{<前>}{<後>}{<対象>}<対象> の文字列に含まれる全ての <前> に一致する部分を <対象> に置換した文字列を結果として返す。
  • 置換の結果の文字列はそれ以上置換の対象とならない。((つまり sedPerls///g と同じ規定。もちろんこの問題は正規表現は関係ないが。))
  • 「文字列」は英数字と「LaTeX で非特殊な記号」(空白は含まない)のみ*1から成ると仮定してよい。
  • 結果の返し方の規約は次の何れかにする。
    • (a) 結果の文字列をトークン列マクロ \result に格納する。(つまり、マクロ \result の一回展開形が「結果の文字列」になるようにする。)
    • (b) 完全展開可能*2であり、完全展開すると「結果の文字列」が得られる。

実行例(LaTeX の場合)

[規約 (a) の場合]
\replaceWordAll{people}{idiots}
  {government-of-the-people-by-the-people-for-the-people}
\typeout{\result}
[規約 (b) の場合]
\typeout{\replaceWordAll{people}{idiots}
  {government-of-the-people-by-the-people-for-the-people}}
% いすれの場合も次の文字列が表示されればよい.
% government-of-the-idiots-by-the-idiots-for-the-idiots

解く人にとって意味があるのならば、何を使っても構わないだろう。

…といっても、LuaTeX で Lua 呼出を使うのは面白くないので、この方面の回答はさっさと済ませてしまおう。

% プレアンブル部
\usepackage{luacode}
%%<*> \replaceWordAll ; 規約(b)に従う
\newcommand\replaceWordAll[3]{\directlua{
  zrpuz_replace_word_all(% エスケープを忘れずに
    \zrpuz@esc{#1}, \zrpuz@esc{#2}, \zrpuz@esc{#3})
}}
\def\zrpuz@esc#1{"\luatexluaescapestring{#1}"}
\begin{luacode*} -- Lua コード
function zrpuz_replace_word_all (from, to, str)
    from = from:gsub("%W", "%%%0") -- quotemeta する
       -- 余分の括弧は gsub() 戻り値を単一値に限定するため
    tex.sprint((str:gsub(from, to)))
end
\end{luacode*}

expl3 で l3regex を使うのも面白くないので済ませてしまおう。*3

% プレアンブル部
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\tl_clear_new:N \result % 結果格納変数
\tl_new:N \l_zrpuz_from_tl % from を quotemeta した結果
\tl_new:N \l_zrpuz_rxqm_tl % quotemeta 用の正規表現(コンパイル済)
%% \l_zrpuz_rxqm を用意する
\regex_set:Nn \l_zrpuz_rxqm_tl { \W }
%%<*> \replaceWordAll ; 規約(a)に従う
\cs_new:Npn \replaceWordAll #1#2#3 {%
    % quotemeta の処理
    \tl_set:Nn \l_zrpuz_from_tl {#1}
    \regex_replace_all:NnN \l_zrpuz_rxqm_tl { \\\0 } \l_zrpuz_from_tl
    % 対象文字列の処理
    \tl_set:Nn \result {#3}
    \exp_args:No \regex_replace_all:nnN { \l_zrpuz_from_tl } {#2} \result
}
\ExplSyntaxOff

*1:つまりカテゴリコード 11 または 12 の文字トーク

*2:expl3 で言えば「制限付展開可能」、つまり x-展開可能のこと。

*3:先のものもと同様、わざわざ強力な正規表現の機能を殺すために quotemeta で頑張らないといけないというのが何となく嫌だ。