Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

% 暫定

このセクションは、価値が疑わしい、または収録するにはニッチすぎる可能性があるパターンや技法のためのものです。

そろばんカウンター

暫定: より説得力のある例が必要です。Ook! マクロの重要な部分ではあるものの、Rust のグループで示されていないネストしたグループにマッチすることは十分に珍しく、収録に値しない可能性があります。

注記: このセクションでは、プッシュダウン蓄積インクリメンタル TT muncherを理解していることを前提としています。

macro_rules! abacus {
    ((- $($moves:tt)*) -> (+ $($count:tt)*)) => {
        abacus!(($($moves)*) -> ($($count)*))
    };
    ((- $($moves:tt)*) -> ($($count:tt)*)) => {
        abacus!(($($moves)*) -> (- $($count)*))
    };
    ((+ $($moves:tt)*) -> (- $($count:tt)*)) => {
        abacus!(($($moves)*) -> ($($count)*))
    };
    ((+ $($moves:tt)*) -> ($($count:tt)*)) => {
        abacus!(($($moves)*) -> (+ $($count)*))
    };

    // 最終結果がゼロかどうかを確認する。
    (() -> ()) => { true };
    (() -> ($($count:tt)+)) => { false };
}

fn main() {
    let equals_zero = abacus!((++-+-+++--++---++----+) -> ());
    assert_eq!(equals_zero, true);
}

この技法は、ゼロまたはゼロ付近から始まり、次の操作をサポートしなければならない可変のカウンターを追跡する必要がある場合に使用できます。

  • 1 増やす。
  • 1 減らす。
  • ゼロ(または他の任意の固定された有限値)と比較する。

n は、グループ内に格納された特定のトークンの n 個のインスタンスで表されます。変更は再帰とプッシュダウン蓄積を使用して行います。使用するトークンが x であると仮定すると、上記の操作は次のように実装されます。

  • 1 増やす: ($($count:tt)*) にマッチし、(x $($count)*) に置換する。
  • 1 減らす: (x $($count:tt)*) にマッチし、($($count)*) に置換する。
  • ゼロと比較する: () にマッチする。
  • 1 と比較する: (x) にマッチする。
  • 2 と比較する: (x x) にマッチする。
  • (以下同様...)

このように、カウンターに対する操作は、そろばんのようにトークンを前後にはじくようなものです。1

負の値を表したい場合、-n別のトークンの n 個のインスタンスとして表すことができます。上の例では、+nn 個の + トークンとして格納され、-mm 個の - トークンとして格納されます。

この場合、操作は少し複雑になります。カウンターが負の場合、インクリメントとデクリメントは実質的に通常の意味が逆になります。つまり、正と負のトークンとしてそれぞれ +- を用いる場合、操作は次のように変わります。

  • 1 増やす:
    • () にマッチし、(+) に置換する。
    • (- $($count:tt)*) にマッチし、($($count)*) に置換する。
    • ($($count:tt)+) にマッチし、(+ $($count)+) に置換する。
  • 1 減らす:
    • () にマッチし、(-) に置換する。
    • (+ $($count:tt)*) にマッチし、($($count)*) に置換する。
    • ($($count:tt)+) にマッチし、(- $($count)+) に置換する。
  • 0 と比較する: () にマッチする。
  • +1 と比較する: (+) にマッチする。
  • -1 と比較する: (-) にマッチする。
  • +2 と比較する: (++) にマッチする。
  • -2 と比較する: (--) にマッチする。
  • (以下同様...)

先頭の例では、いくつかのルールをまとめていることに注意してください(たとえば、() に対するインクリメントと ($($count:tt)+) に対するインクリメントを、($($count:tt)*) に対するインクリメントにまとめています)。

カウンターの実際のを取り出したい場合は、通常のカウンターマクロを使用して行えます。上の例では、終端ルールを次のように置き換えることができます。

macro_rules! abacus {
    // ...

    // カウンターを整数式として抽出する。
    (() -> ()) => {0};
    (() -> (- $($count:tt)*)) => {
        {(-1i32) $(- replace_expr!($count 1i32))*}
    };
    (() -> (+ $($count:tt)*)) => {
        {(1i32) $(+ replace_expr!($count 1i32))*}
    };
}

macro_rules! replace_expr {
    ($_t:tt $sub:expr) => {$sub};
}

JFTE: 厳密に言えば、上記の abacus! の定式化は不必要に複雑です。マクロ内でカウンターの値に対してマッチする必要がないのであれば、繰り返しを使用してはるかに効率的に実装できます。

macro_rules! abacus {
    (-) => {-1};
    (+) => {1};
    ($($moves:tt)*) => {
        0 $(+ abacus!($moves))*
    }
}

  1. このかなり苦しい理屈は、この名前の本当の理由を覆い隠しています。つまり、名前に「token」が入るものをまたさらに増やすのを避けるためです。今すぐ意味飽和を避ける方法について執筆者に相談しましょう!

    公平を期すなら、これは"単項カウント"と呼ぶこともできたでしょう。