マクロツイーター

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

「LaTeX で OOP」(PGF の oo モジュール)で getter を追加してみる

注意: これはネタです。

アレとかコレ(コメントも)とかは読んでいる前提で。)

PGF の oo モジュールによる OOP では、属性(インスタンス変数)に対するクラス外のコードでのアクセスを認めていない。だから、アクセサメソッド(getter/setter)を用意する必要がある。それはいいとして、少し不便なのは、oo モジュールの設計上、メソッドは決して展開可能にならないので、「属性の値自身に展開される」ような getter が書けない。例えば次のようなコードはエラーになる。((本題とは関係のない話。PGF/TikZ にある oo モジュールの例では改行文字の無効化(行末の〈%〉)を全くしていない。これは既定で無効化されるのだと思っていると、どうやら全くそうではなく、無効化を行わないと意図しない空白が実際に入ってしまう。この記事のコードでは \endlinechar 設定を用いた。))

\documentclass{article}
\usepackage{pgf}\usepgfmodule{oo}
\endlinechar=-1 %
\pgfooclass{pair}{% 何の変哲もないペア(対)です
  \attribute first;
  \attribute second;
  \method pair(#1,#2){% コンストラクタ
    \pgfooset{first}{#1}
    \pgfooset{second}{#2}
  }
  \method form(){% 中身を出力するメソッド
    [\pgfoovalueof{first},\pgfoovalueof{second}]
  }
  \method get-first(){% first の getter のはず
    \pgfoovalueof{first}
  }
}
\endlinechar=13 %
\begin{document}
\pgfoonew\pairA=new pair(99,6)% インスタンスを作る
\pairA.form() % 出力はできる
\newcount\countA
\countA=\pairA.get-first()\relax% 99 を代入したいのだがエラー!
\the\countA
\end{document}

従って、getter は次のように実装する必要がある。

  \method let-first-to(#1){% first の値をマクロ #1 に代入する
    \pgfooget{first}{#1}
  }

これで、「値を取得する」ことができる。((ここで \pairA.let-first-to(\countA) とするのは \def\countA{99} の意味になるので正しくない。))

\pairA.let-first-to(\thevalue)%
\countA=\thevalue\relax
\the\countA %==> 99

必要な機能は実現されているが、何か気持ち悪い。

で、作ってみた。

\pgfooclass の本体中で、属性 foo を定義(\attribute foo;)した後で

  \accessor foo;

を宣言しておくと、次のアクセサメソッド(\obj はハンドラ)が定義される。(jQuery みたいに、1 つのメソッドで引数の有無で getter/setter の区別をするようにしてみた。((そうすると、空列を設定したい場合に困る…ということは後で気付いた。非展開の \obj.foo*() は常に setter なので空列を代入したい場合はこちらを使うとよい。あるいは \empty を適宜使うという方法もある。)))

  • \obj.foo() : 属性 foo の値(トークン列)に展開される。
  • \obj.foo(<値>) : 属性 foo に引数の値を完全展開したトークン列を代入する。
  • \obj.foo*(<値>) : 属性 foo に引数の値(展開しない)を代入する。
\documentclass{article}
\usepackage{pgf}\usepgfmodule{oo}
\usepackage{bxpgfooacc}
\endlinechar=-1 %
\pgfooclass{pair}{
  \attribute first;
  \accessor first;% アクセサ first() を定義
  \attribute second;
  \accessor second;% アクセサ second() を定義
  \method pair(#1,#2){
    \pgfooset{first}{#1}
    \pgfooset{second}{#2}
  }
  \method form(){
    [\pgfoovalueof{first},\pgfoovalueof{second}]
  }
}
\endlinechar=13 %
\begin{document}
\pgfoonew\pairA=new pair(99,6)%
\pairA.form()
\pairA.first({\pairA.second()})% 展開して代入する
\pairA.second*(\seven)% 展開せず代入する(\seven は未定義)
\def\seven{7}%
\pairA.form() %==> [6,7] : second は \seven でここで 7 に展開される
\newcount\result % 算術演算をしてみる
\result=\pairA.first()\relax
\multiply\result by \pairA.second()\relax
\the\result %==> 42 : 6×7 を計算した
\end{document}

私の感覚では、getter がそれ自身展開可能であることは必須ではないと思う。でも、そもそも「値が取得できないもの」をフィールドとか変数とか関数の戻り値とか呼ぶのは違和感を感じる。