首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >无需包装器即可轻松实现此"Magma“特征

无需包装器即可轻松实现此"Magma“特征
EN

Stack Overflow用户
提问于 2021-07-04 20:56:08
回答 2查看 52关注 0票数 1

我的目标是创建一个将Couple<Couple<Couple<T>>>类型的元素转换为T类型的元素的函数

大多数时候,这看起来真的很容易做到。实际上,如果您有一个操作符或函数将2个T组合成1个T,则可以递归地执行此操作。

代码语言:javascript
复制
If I want to fusion ((1 2) (3 4)) into a single number, 
I can use the "+" operator recursively:
(1 + 2) + (3 + 4) = 10

因此,我创建了一个名为Magma (带有组合操作的类型)的特征,并创建了一个递归地将两个特征融合在一起的函数。

代码语言:javascript
复制
// simple couple type
struct Couple<T>(T, T);

trait Magma {
    // a magma is just a type with a function   (S, S) -> S (for ex. the "+" operation)
    fn combine(a: Self, b: Self) -> Self;
}

// fn fusion<T>(Couple<Couple<Couple<T>>>) -> T where T: Magma {}

但问题是,要将此fusion函数与Couple<Couple<bool>>类型一起使用,我必须为bool实现Magma

代码语言:javascript
复制
impl Magma for bool {
    fn combine(a: bool, b: bool) -> bool {
        a && b
    }
}

但是有很多方法可以将两个布尔值组合成1:"or","and","xor“……

我不能为这些函数中的每一个都实现Magma

因此,我目前的方法是对bools使用包装器:

代码语言:javascript
复制
struct OrWrapper(bool);

impl Magma for OrWrapper {
    fn combine(a: Self, b:Self) -> Self {
        OrWrapper(a.0 || b.0)
    }
}


struct AndWrapper(bool);

impl Magma for AndWrapper{
    fn combine(a: Self, b:Self) -> Self {
        AndWrapper(a.0 && b.0)
    }
}

但这确实是重复和痛苦的编写,我想知道是否有更优雅的解决方案。

有什么想法吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-07-05 01:21:50

包装器类型几乎肯定是要走的路。岩浆被定义为一对:一个集合(即类型)和一个运算符,您必须以某种方式捕获这两者。

您可以更改Magma特征以使其更易于使用,这样它就可以接受内部类型并在内部转换为包装器:

代码语言:javascript
复制
trait Magma: Sized {
    fn combine(self, b: impl Into<Self>) -> Self;
}

如果重复定义这些包装器类型是您所担心的,那么您可以使用宏来生成它们:

代码语言:javascript
复制
macro_rules! magma {
    ($($ty: ty as $wrapper: ident => $op: path),* $(,)?) => {
        $(
            // a new wrapper type
            #[derive(Copy, Clone, Debug)]
            pub struct $wrapper($ty);
            
            impl Magma for $wrapper {
                fn combine(self, b: impl Into<Self>) -> $wrapper {
                    $wrapper($op(&self.0, &b.into().0))
                }
            }
        )*
    }
}

magma! {
    bool as BoolAnd => std::ops::BitAnd::bitand,
    bool as BoolOr => std::ops::BitOr::bitor,
    u32 as U32Add => std::ops::Add::add,
    u32 as U32Mul => std::ops::Mul::mul,
}

为进一步方便起见,您可能还想为这些类型实现From转换、Deref和其他特性,如Display

代码语言:javascript
复制
macro_rules! magma {
    ($($ty: ty as $wrapper: ident => $op: path),* $(,)?) => {
        $(
            // a new wrapper type
            #[derive(Copy, Clone, Debug)]
            pub struct $wrapper($ty);
            
            // conversion from the raw type to the wrapper
            impl From<$ty> for $wrapper {
                fn from(x: $ty) -> $wrapper { $wrapper(x) }
            }
            
            // conversion from the wrapper type to the inner type
            impl From<$wrapper> for $ty {
                fn from(w: $wrapper) -> $ty { w.0 }
            }
            
            // Deref to the inner type for convenience
            impl std::ops::Deref for $wrapper {
                type Target = $ty;
                fn deref(&self) -> &$ty { &self.0 }
            }
            
            // Delegate to the inner type for display
            impl std::fmt::Display for $wrapper {
                fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                    self.0.fmt(f)
                }
            }
            
            impl Magma for $wrapper {
                fn combine(self, b: impl Into<Self>) -> $wrapper {
                    $wrapper($op(&self.0, &b.into().0))
                }
            }
        )*
    }
}

用法:

代码语言:javascript
复制
magma! {
    bool as BoolAnd => std::ops::BitAnd::bitand,
    bool as BoolOr => std::ops::BitOr::bitor,
    u32 as U32Add => std::ops::Add::add,
    u32 as U32Mul => std::ops::Mul::mul,
}

fn main() {
    println!("{}", BoolOr(true).combine(false)); // true
}
票数 3
EN

Stack Overflow用户

发布于 2021-07-05 18:06:51

Peter Hall的另一种方法是创建一个结合了值和操作的类型-并对操作使用零大小的类型。这意味着减少了对宏的需求。

使用像Peters这样的特征,这可能看起来像这样:

代码语言:javascript
复制
trait Magma: Sized {
    fn combine(self, b: impl Into<Self>) -> Self;
}

trait ExternalMagma<T> {
    fn combine(a: T, b: T) -> T;
}

#[derive(Copy, Clone, Debug)]
struct ValueWithOp<T,F: ExternalMagma<T>>(T, F);

impl<T, F> Magma for ValueWithOp<T, F>
where
    F: ExternalMagma<T>
{
    fn combine(self, b: impl Into<Self>) -> Self {
        let f = self.1;
        ValueWithOp(F::combine(self.0, b.into().0), f)
    }
}

impl<T,F> From<T> for ValueWithOp<T,F>
where F: Default + ExternalMagma<T>
{
    fn from(v:T) -> ValueWithOp<T,F> {
        ValueWithOp(v, F::default())
    }
}

impl<T,F> ValueWithOp<T,F>
where F: ExternalMagma<T> {
    fn unwrap(self) -> T {
        self.0
    }
}

#[derive(Copy, Clone, Debug, Default)]
struct BoolOrOp;
impl ExternalMagma<bool> for BoolOrOp {
   fn combine(a: bool, b: bool) -> bool { a || b }
}

#[derive(Copy, Clone, Debug, Default)]
struct MulOp;
impl ExternalMagma<f32> for MulOp {
   fn combine(a: f32, b: f32) -> f32 { a * b }
}
impl ExternalMagma<i32> for MulOp {
   fn combine(a: i32, b: i32) -> i32 { a * b }
}

fn main() {
    println!("{}", ValueWithOp::<bool, BoolOrOp>::from(true).combine(false).unwrap() ); // true
    println!("{}", ValueWithOp::<i32, MulOp>::from(2).combine(3).unwrap() ); // 6
}

你可以在playground上看到这个

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68244931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档