如何对宏规则的参数对进行操作?

问题描述

我正在编译时使用常量泛型和宏构建一个简单的前馈神经网络。这是一堆一个一个的矩阵。

我创建了 network! 宏,其工作方式如下:

network!(2,4,1)

第一项是输入的数量,其余的是每层的神经元数量。宏如下所示:

#[macro_export]
macro_rules! network {
    ( $inputs:expr,$($outputs:expr),* ) => {
        {
            Network {
                layers: [
                    $(
                        &Layer::<$inputs,$outputs>::new(),)*
                ]
            }
        }
    };
}

它声明了一个层元素数组,它使用const泛型在每一层上有一个固定大小的权重数组,第一个类型参数是它期望的输入数量,第二个类型参数是输出数量

这个宏产生以下代码

Network {
    layers: [
         &Layer::<2,4>::new(),&Layer::<2,1>::new(),]
}

这是完全错误的,因为对于每一层,输入的数量应该是前一层的输出数量,就像这样(注意 2 -> 4):

Network {
    layers: [
         &Layer::<2,&Layer::<4,]
}

为此,我需要在每次迭代时将 $inputs 值替换为 $outputs 的值,但我不知道该怎么做。

解决方法

您可以匹配两个前导值,然后匹配其余所有值。对这两个值做一些特定的事情并递归调用宏,重用第二个值:

struct Layer<const I: usize,const O: usize>;

macro_rules! example {
    // Do something interesting for a given pair of arguments
    ($a:literal,$b:literal) => {
        Layer::<$a,$b>;
    };

    // Recursively traverse the arguments
    ($a:literal,$b:literal,$($rest:literal),+) => {
        example!($a,$b);
        example!($b,$($rest),*);
    };
}

fn main() {
    example!(1,2,3);
}

扩展宏导致:

fn main() {
    Layer::<1,2>;
    Layer::<2,3>;
}
,

对于那些感兴趣的人,根据@Shepmaster 的回答,我终于能够像这样填充我的网络:

struct Network<'a,const L: usize> {
    layers: [&'a dyn Forward; L],}

macro_rules! network {
    // Recursively accumulate token tree
    (@accum ($a:literal,$($others:literal),+) $($e:tt)*) => {
        network!(@accum ($b,$($others),*) $($e)*,&Layer::<$a,$b>::new())
    };

    // Latest iteration,convert to expression
    (@accum ($a:literal,$b:literal) $($e:tt)*) => {[$($e)*,$b>::new()]};

    // Entrance
    ($a:literal,+) => {
        Network {
            layers: network!(@accum ($b,*) &Layer::<$a,$b>::new())
        }
    };
}

对于 network!(2,3,4,5,1),它转换为:

Network {
     layers:
          [&Layer::<2,3>::new(),&Layer::<3,4>::new(),&Layer::<4,5>::new(),&Layer::<5,1>::new()]
};