我有一個列舉,其變體具有相同的結構(這些變體可以切換角色),并且出現在一個大型資料結構中。
我需要通過交換變體的角色來進行某些計算(一個相當長的程式)。 由于資料量的原因,我應該避免直接在資料上進行這種互換。
為了盡量減少代碼的長度,并使未來的演化更加可靠,我決定以一種通用的方式來編程。
到目前為止,我已經嘗試了使用宏的東西。 下面的代碼是我在各種變體中進行通用實作的一個例子。(playground)
// enum with same variants shapes。
#[derive(Debug, Clone)]
enum Choice{
A{ val: f32, },
B{ val: f32, },
}
use Choice::{A,B,};
//有待實作的代碼trait MyTrait<const P: usize> { // P是一個包絡引數。
fn eval(&self) -> f32;
fn zoom(&mut self) 。
fn transmute(self) -> Self;
}
// variant permutation macros
macro_rules! choice_a {
(0 | val: $a:ident) => (A{val: $a})。
(1 | val: $a:ident) => (B{val: $a});
(0 | val: ref $a:ident) => (A{val: ref $a}) 。
(1 | val: ref $a:ident) => (B{val: ref $a}) 。
(0 | val: ref mut $a:ident) => (A{val: ref mut $a};
(1 | val: ref mut $a:ident) => (B{val: ref mut $a})。
(0 | val: $a:expr) => (A{val: $a})。
(1 | val: $a:expr) => (B{val: $a})。
}
macro_rules! choice_b {
(0 | val: $a:ident) => (B{ val: $a}) 。
(1 | val: $a:ident) => (A{ val: $a})。
(0 | val: ref $a:ident) => (B{ val: ref $a})。
(1 | val: ref $a:ident) => (A{ val: ref $a})。
(0 | val: ref mut $a:ident) => (B{ val: ref mut $a};
(1 | val: ref mut $a:ident) => (A{ val: ref mut $a}) 。
(0 | val: $a:expr) => (B{ val: $a})。
(1 | val: $a:expr) => (A{ val: $a})。
}
///為通用實作的宏。
macro_rules! mytrait {
($p:tt) => (
impl MyTrait<$p> for Choice {
fn eval(&self) -> f32 {
match *self {
choice_a!{$p | val: ref va} => { va.powi(2) },
choice_b! {$p | val: ref va} => { va.sqrt() },
}
}
fn zoom(&mut self) {
match *self {
choice_a!{$p | val: ref mut va} => { *va *= 2.0; },
choice_b! {$p | val: ref mut va} => { *va /= 2.0; },
}
}
fn transmute(self) -> Self {
match self {
choice_a! {$p | val: va} => { choice_b! {$p | val: va 1.0}. },
choice_b! {$p | val: va} => { choice_a! {$p | val: va - 1.0}. },
}
}
}
);
}
mytrait!(0); // no permutation case。
mytrait!(1); //permutation case。
fn main(){
for x in vec! [Choice::A{ val: 16.0}, Choice::B{ val: 16.0] {
let mut y = x.clone()。
println!("Variants roles are not permuted:")。
println!("y = {:?}", y) 。
println!("eval y = {:?}", MyTrait::<0> ::eval(& y)。
MyTrait::<0>::zoom(&mut y)。
println!("zoomed y = {:?}", y) 。
let y = MyTrait::<0>::transmute(y)。
println!("transmute zoomed y = {:?}", y) 。
println!("-------------------------");
println!("Variants roles are permuted: ")。
let mutz = x;
println!("z = {:?}", z) 。
println!("eval z = {:?}", MyTrait::<1> ::eval(& z)。
MyTrait::<1>::zoom(&mut z)。
println!("zoomed z = {:?}", z) 。
let z = MyTrait::<1>::transmute(z)。
println!("transmute zoomed z = {:?}", z) 。
println!("=====================================");
}
其結果是:
變體角色沒有被滲透。
y = A { val: 16.0 }
評價 y = 256.0
zoomed y = A { val: 32.0 }
轉化的zoomed y = B { val: 33.0 }
-------------------------
變體角色被透視。
z = A { val: 16.0 }
評價 z = 4.0 4.0
縮放 z = A { val: 8.0 }
轉化后的z = B { val: 7.0 }
=====================================
變體角色沒有被透射。
y = B { val: 16.0 }
評價 y = 4.0 }
zoomed y = B { val: 8.0 }
轉化放大的y = A { val: 7.0 }
-------------------------
變體角色被透視。
z = B { val: 16.0 }
評估 z = 256.0 256.0
縮放 z = B { val: 32.0 }
轉化后的z = A { val: 33.0 }
=====================================
我的代碼可以作業,但并不完全令人滿意。 的確,我的choice_a!和choice_b!宏沒有進化性,使用起來很死板,產生的不是整個模式就是列舉的完整定義。 如果這些宏只產生列舉的名稱會更好。 例如,最好能夠寫出choice_a!($p) { val: va - 1.0}而不是choice_a!{$p | val: va - 1.0},最好能夠寫出choice_b! ($p){ val: ref mut va}而不是choice_b!{$p | val: ref mut va}。
是否可以這樣做? 除了宏之外,是否還有其他方法可以在不同的變體中進行這種通用的實作?
uj5u.com熱心網友回復:
不確定我是否在評論這個問題所要解決的事情(所以如果我說得不對,請忽略這個答案)。但如果完全避免使用宏,并嘗試像這樣的方法,不是更容易(或至少更易讀)嗎?
//enum with same variants shapes
#[derive(Debug, Clone)]
enum Choice{
A{ val: f32, },
B{ val: f32, },
}
use Choice::{A,B,};
//有待實作的代碼trait MyTrait<const P: bool> { // P是一個包絡引數。
fn eval(&self) -> f32;
fn zoom(&mut self) 。
fn transmute(self) -> Self;
}
impl Choice {
fn as_val(&self) -> &f32 {
match self {
Choice::A { val } => val,
Choice::B { val } => val,
}
}
fn as_val_mut(&mut >self) -> &mut f32 {
match self {
Choice::A { val } => val,
Choice::B { val } => val,
}
}
/// 如果P=false,主要選擇是A,次要選擇是B;如果P=true,價值會被調換。
fn is_primary<const P。bool>(&self) -> bool {
match self {
Choice::A { . } => !P,
選擇::B { . } => P。
}
}
fn flip_type(self) ->/span> Choice {
match self {
Choice::A { val } => Choice::B { val },
Choice::B { val } => Choice::A { val }
}
}
}
impl <const P: bool> MyTrait< P> for Choice {
fn eval(&self) -> f32 {
if self.is_primary::<P> () {
self.as_val().powi(2)。
} else {
self.as_val().sqrt()
}
}
fn zoom(&mut self) {
if self.is_primary::<P> () {
*self.as_val_mut() *= 2.0;
} else {
*self.as_val_mut() /= 2.0。
}
}
fn transmute(self) -> Self {
let is_primary = self.is_primary::<P>()。
let mut copy = self.flip_type() 。
if is_primary {
*copy.as_val_mut() = 1.0;
} else {
*copy.as_val_mut() -= 1.0;
};
復制
}
}
fn main( ) {
for x in vec! [Choice::A{ val: 16.0}, Choice::B{ val: 16.0] {
let mut y = x.clone()。
println!("Variants roles are not permuted:")。
println!("y = {:?}", y) 。
println!("eval y = {:?}", MyTrait::<false>::eval(&y)。
MyTrait::<false>::zoom(&mut y)。
println!("zoomed y = {:?}", y) 。
let y = MyTrait::<false>::transmute(y)。
println!("transmute zoomed y = {:?}", y) 。
println!("-------------------------");
println!("Variants roles are permuted: ")。
let mutz = x;
println!("z = {:?}", z) 。
println!("eval z = {:?}", MyTrait::<true> ::eval(&z)。
MyTrait::<true>::zoom(&mut z)。
println!("zoomed z = {:?}", z) 。
let z = MyTrait::<true>::transmute(z)。
println!("transmute zoomed z = {:?}", z) 。
println!("=====================================");
}
這段代碼給你同樣的結果。is_primary檢查應該編譯成與你原來的匹配宏基本相同的東西,所以性能應該是相似的(但如果你想確定,我可以檢查)。它略長,但總的來說,我發現它更易讀,而且新方法在實作MyTrait之外也很有用。
uj5u.com熱心網友回復:
用一個簡單的宏改進來回答:(playground)
//enum with same variants shapes。
#[derive(Debug, Clone)]
enum Choice{
A{ val: f32, },
B{ val: f32, },
}
use Choice::{A,B,};
//變體包絡宏;*前的空格是必須的!
macro_rules! a {
(0 | $($t:tt) *) => (A{$($t) *})。
(1 | $($t:tt) *) => (B{$($t) *})。
}
macro_rules! B {
(0 | $($t:tt) *) => (B{$($t) *}) 。
(1 | $($t:tt) *) => (A{$($t) *})。
}
//待實作的代碼。
trait MyTrait<const P: usize> { // P是一個包絡引數。
fn eval(&self) -> f32;
fn zoom(&mut self) 。
fn transmute(self) -> Self;
}
/span>通用實作的宏。
macro_rules! mytrait {
($p:tt) => (
impl MyTrait<$p> for Choice {
fn eval(&self) -> f32 {
match *self {
a!{$p | val: ref va} => { va.powi(2) },
b! {$p | val: ref va} => { va.sqrt() },
}
}
fn zoom(&mut self) {
match *self {
a!{$p | val: ref mut va} => { *va *= 2.0; },
b!{$p | val: ref mut va} => { *va /= 2.0; },
}
}
fn transmute(self) -> Self {
match self {
a!{$p | val: va} => { b!{$p | val: va 1.0}. },
b!{$p | val: va} => { a!{$p | val: va - 1.0}. },
}
}
}
);
}
mytrait!(0); // no permutation case。
mytrait!(1); // permutation case。
上一個答案:
用enum Choice{ A{ val: f32, }, B{ val: f32, }, }代替type Choice = (Variant,Data),用enum Variant{ A, B, }和struct Data { val: f32 }取代。(playground)
// variant label。
#[derive(Debug, Clone, Copy,)]/span>
enum Variant{ A, B, }
use Variant::{A,B,};
//資料域
#[derive(Debug, Clone,)]
struct Data { val: f32 }
// enum like type
type Choice = (Variant,Data)。
macro_rules! a { (0) => (A); (1) => (B); }
macro_rules! b { (0) => (B); (1) => (A); }
//有待實作的代碼。
trait MyTrait<const P: usize> { // P是一個包絡引數。
fn eval(&self) -> f32;
fn zoom(&mut self) 。
fn transmute(self) -> Self;
}
/span>通用實作的宏。
macro_rules! mytrait {
($p:tt) => (
impl MyTrait<$p> for Choice {
fn eval(&self) -> f32 {
match *self {
(a!($p), Data{val: ref v}) => { v.powi(2) },
(b! ($p), Data{val: ref v}) => { v.sqrt() },
}
}
fn zoom(&mut self) {
match *self {
(a! ($p), Data{val: ref mut v}) => { *v *= 2.0; },
(b! ($p), Data{val: ref mut v}) => { *v /= 2.0; },
}
}
fn transmute(self) -> Self {
match self {
(a!($p), Data{val: v}) => (b![$p], Data{val: v 1.0}) 。
(b!($p), Data{val: v}) => (a! [$p], Data{val: v - 1.0}),
}
}
}
);
}
mytrait!(0); // no permutation case。
mytrait!(1); // permutation case。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/318628.html
標籤:
上一篇:kotlin的compareTo函式是否支持泛型,或者我做錯了什么?
下一篇:來自一個通用介面的擴展函式
