マクロツイーター

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

それでも TeX でプログラミングしたい人のための何か (7)

ステップ 4 : 式と実行制御の関連の処理

元のプログラムはシリーズ(5)の最後に掲載した eltaso2.lua

式を分解する

整数の四則演算について、TeX には単純な + - * / は無くて、(C言語の)複合代入 += -= *= /= に相当する機能だけが用意されている。(Lua には複合代入文がない(a += b は単純に a = a + b と書く)ので、この節では C言語風の記述を用いる。)だから全ての整数の式を

〈変数1〉〈= += -= *= /= の何れか〉〈- か空か〉〈変数2/定数〉

のみを用いた形(右辺に単項マイナスが使えることに注意)に分解する必要がある。幾つか例を示す。

a = b + c
↓
a = b; a += c
s = (a + b) * h / 2
↓
s = a; s += b; s *= h; s /= 2
nq = x * x + y * y
↓ (変数 t を導入する)
nq = x; nq *= x; t = y; t *= y; nq += t
u = nz - u * u
↓
u *= -u; u += nz

なお、四則演算以外の演算は直接にはサポートされないので、そういう演算がある場合は、ステップ 2 の段階で関数にして分離しておく必要がある。(eltaso1.luaremainder() 関数のように。)

文字列の式に関しては、連接演算についてはそのままの形で構わない。(\edef で多数の項を一度に連接することが可能だから。)それ以外の演算は直接にはサポートされないのでやはり分離する必要がある。ただし文字列の演算は初級者には難しいので何らかの既存のパッケージを導入した方がよいかも知れない。

以上で式の分解の話を終えるが、実のところ、eltaso2.lua には分解すべき式が存在しない。だからここでは何もすることがない。

for 文等を while 文に置き換える

今回の TeX(on LaTeX)への変換作業においては、ループ構文を全て \@whilenum で表すことにする。従って、while 文以外のループ構文を全て while に置き換えておく。(for-each 構文はそのまま残す。)例えば、eltaso1.lua には以下のような for 文があるが、

  for j = 1, max do
    eltaso_name(j)
    print(sres)
  end

これを次のような while 文に置き換えよう。((最も素直に書くと、「j = 1; while j <= max do ... ; j = j + 1; end」となるはずだが、TeX では「<=」が扱い難いので、敢えてこの形にしている。))

  j = 0
  while j < max do
    j = j + 1
    eltaso_name(j)
    print(sres)
  end
変換後のプログラム
[eltaso3.lua]
-- [変数一覧]
-- 整数: max, j, ires(戻り値)
-- 文字列: dm, dc, dx, di, km, kc, kx, sres(戻り値)
-- 配列: alpha_name, digit

alpha_name = {
  [0] = "ぜっと",
  "えー", "びー", "しー", "でー", "いー", "えふ", "じー",
  "えいち", "あい", "じぇー", "けー", "える", "えむ", "えぬ",
  "おー", "ぴー", "きゅー", "あーる", "えす", "てぃー", "ゆー",
  "ぶい", "だぶりゅー", "えっくす", "わい"
}
digit = {
  [0] = "",
  "一", "二", "三", "四", "五", "六", "七", "八", "九"
}

function knumeral(_1)
  split_digit(_1)
  knum_pos(dm, "千"); km = sres
  knum_pos(dc, "百"); kc = sres
  knum_pos(dx, "十"); kx = sres
  sres = km .. kc .. kx .. digit[tonumber(di)]
end
function split_digit(_1) -- TeXでは再実装が必要
  dm, dc, dx, di =  -- 直接に目的の変数に値を返す
  ("%04d"):format(_1):match("^(.)(.)(.)(.)$")
end
function knum_pos(_1, _2)
  if tonumber(_1) == 0 then sres = ""
  elseif tonumber(_1) == 1 then sres = _2
  else sres = digit[tonumber(_1)] .. _2
  end
end

function eltaso_name(_1)
  knumeral(_1) -- sres に返る
  remainder(_1, 26) -- ires に返る
  sres = sres .. "反田" .. alpha_name[ires]
end
function remainder(_1, _2) -- TeXでは再実装が必要
  ires = _1 % _2
end

function eltaso(_1)
  max = _1
  if max > 9999 then max = 9999 end
  j = 0
  while j < max do
    j = j + 1
    eltaso_name(j)
    print(sres)
  end
end

eltaso(1000)