Построить все пары элементов (квадратичное множество) в декларативном макросе - PullRequest
0 голосов
/ 06 февраля 2019

У меня есть список идентификаторов, и я хочу вызвать макрос для каждой пары идентификаторов из этого списка.Например, если у меня есть a, b и c, я хотел бы сгенерировать это:

println!("{} <-> {}", a, a);
println!("{} <-> {}", a, b);
println!("{} <-> {}", a, c);
println!("{} <-> {}", b, a);
println!("{} <-> {}", b, b);
println!("{} <-> {}", b, c);
println!("{} <-> {}", c, a);
println!("{} <-> {}", c, b);
println!("{} <-> {}", c, c);

Конечно, это фиктивный пример.В моем реальном коде идентификаторы являются типами, и я хочу создать impl блоков или что-то в этом роде.

Моя цель - перечислить каждый идентификатор только один раз.В моем реальном коде у меня около 12 идентификаторов, и я не хочу вручную записывать все пары 12² = 144.Поэтому я подумал, что макрос может мне помочь.Я знаю, что это можно решить с помощью всех мощных процедурных макросов, но я надеялся, что это также возможно с помощью декларативных макросов (macro_rules!).


Я попробовал то, что, по моему мнению, было интуитивно понятным способом обработкиthis (две вложенные "петли") ( Playground ):

macro_rules! print_all_pairs {
    ($($x:ident)*) => {
        $(
            $(
                println!("{} <-> {}", $x, $x);  // `$x, $x` feels awkward... 
            )*
        )*
    }
}

let a = 'a';
let b = 'b';
let c = 'c';

print_all_pairs!(a b c);

Однако, это приводит к этой ошибке:

error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
 --> src/main.rs:4:14
  |
4 |               $(
  |  ______________^
5 | |                 println!("{} <-> {}", $x, $x);
6 | |             )*
  | |_____________^

Я думаю, это делает видсмысл, поэтому я попробовал что-то еще ( Playground ):

macro_rules! print_all_pairs {
    ($($x:ident)*) => {
        print_all_pairs!(@inner $($x)*; $($x)*);
    };
    (@inner $($x:ident)*; $($y:ident)*) => {
        $(
            $(
                println!("{} <-> {}", $x, $y);
            )*
        )*
    };
}

Но это приводит к той же ошибке, что и выше!

Возможно ли это с декларативныммакросы вообще?

1 Ответ

0 голосов
/ 06 февраля 2019

Возможно ли это вообще с декларативными макросами?

Да .

Но (насколько мне известно) мы должны перебирать список один раз с помощью рекурсии голова / хвост, вместо того чтобы везде использовать встроенный механизм $( ... )*.Это означает, что длина списка ограничена глубиной рекурсии макроса.Это не проблема для «только» 12 элементов.

В приведенном ниже коде я отделил функциональность «для всех пар» от печатного кода, передав имя макроса в макрос for_all_pairs.( Детская площадка ).

// The macro that expands into all pairs
macro_rules! for_all_pairs {
    ($mac:ident: $($x:ident)*) => {
        // Duplicate the list
        for_all_pairs!(@inner $mac: $($x)*; $($x)*);
    };

    // The end of iteration: we exhausted the list
    (@inner $mac:ident: ; $($x:ident)*) => {};

    // The head/tail recursion: pick the first element of the first list
    // and recursively do it for the tail.
    (@inner $mac:ident: $head:ident $($tail:ident)*; $($x:ident)*) => {
        $(
            $mac!($head $x);
        )*
        for_all_pairs!(@inner $mac: $($tail)*; $($x)*);
    };
}

// What you actually want to do for each pair
macro_rules! print_pair {
    ($a:ident $b:ident) => {
        println!("{} <-> {}", $a, $b);
    }
}

// Test code
let a = 'a';
let b = 'b';
let c = 'c';

for_all_pairs!(print_pair: a b c);

Этот код печатает:

a <-> a
a <-> b
a <-> c
b <-> a
b <-> b
b <-> c
c <-> a
c <-> b
c <-> c
...