如何从枚举中选择随机值?

时间:2018-01-28 18:45:46

标签: enums rust

spinners包装箱中有一个enum,其中包含大量可能的纺纱工具。

这是枚举(除跳过顶部和底部4之外的所有值):

pub enum Spinners {
    Dots,
    Dots2,
    Dots3,
    Dots4,
    ...
    Shark,
    Dqpb,
    Weather,
    Christmas,
}

新的微调器很容易创建:

extern crate spinners;

use spinners::{Spinner, Spinners};
use std::thread::sleep;
use std::time::Duration;

fn main() {
    let sp = Spinner::new(Spinners::Dots9, "Waiting for 3 seconds".into());
    sleep(Duration::from_secs(3));
    sp.stop();
}

但是,我希望随机选择一个微调器,这不起作用:

let spinner_enum = rng.choose(Spinners).unwrap_or(&Spinners::Dots9);

由于:

error[E0423]: expected value, found enum `Spinners`

let spinner_enum = rng.choose(Spinners).unwrap_or(&Spinners::Dots9);
                              ^^^^^^^^ not a value

如何随机选择枚举值,并使用它来显示随机微调器?

2 个答案:

答案 0 :(得分:15)

你自己的枚举

与Rust中的大多数抽象一样,随机值生成由特征提供支持。对于任何特定类型,实现特征都是相同的,唯一的区别在于特征的方法和类型。

Rand 0.5& 0.6

使用您的枚举作为类型参数实施Distribution。您还需要选择特定类型的分发; Standard是一个很好的默认选择。然后使用任何方法生成值,例如rand::random

extern crate rand;

use rand::{
    distributions::{Distribution, Standard},
    Rng,
};

#[derive(Debug)]
enum Spinner {
    One,
    Two,
    Three,
}

impl Distribution<Spinner> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Spinner {
        match rng.gen_range(0, 3) {
            0 => Spinner::One,
            1 => Spinner::Two,
            _ => Spinner::Three,
        }
    }
}

fn main() {
    let spinner: Spinner = rand::random();
    println!("{:?}", spinner);
}

兰德0.4

为您的枚举实施Rand,然后使用任何方法生成值,例如Rng::gen

extern crate rand;

use rand::{Rand, Rng};

#[derive(Debug)]
enum Spinner {
    One,
    Two,
    Three,
}

impl Rand for Spinner {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        match rng.gen_range(0, 3) {
            0 => Spinner::One,
            1 => Spinner::Two,
            _ => Spinner::Three,
        }
    }
}

fn main() {
    let mut rng = rand::thread_rng();
    let spinner: Spinner = rng.gen();
    println!("{:?}", spinner);
}

导出

rand_derive crate可以消除对某些样板的需求,但对于Rand 0.5不存在。

extern crate rand;
#[macro_use]
extern crate rand_derive;

use rand::Rng;

#[derive(Debug, Rand)]
enum Spinner {
    One,
    Two,
    Three,
}

fn main() {
    let mut rng = rand::thread_rng();
    let spinner: Spinner = rng.gen();
    println!("{:?}", spinner);
}

其他人的枚举

由于您无法控制枚举,因此您必须将某些内容复制到您的代码中才能引用它。您可以从中创建一个枚举数组choose

extern crate rand;

#[derive(Debug)]
enum Spinner {
    One,
    Two,
    Three,
}

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    let spinners = [Spinner::One, Spinner::Two, Spinner::Three];
    let spinner = rng.choose(&spinners).unwrap();
    println!("{:?}", spinner);
}

您可以在本地复制整个枚举,为此实现Rand,然后有一个转换回其他包装箱表示的方法。

extern crate rand;

use rand::{
    distributions::{Distribution, Standard},
    Rng,
};

mod another_crate {
    #[derive(Debug)]
    pub enum Spinner {
        One,
        Two,
        Three,
    }
}

enum Spinner {
    One,
    Two,
    Three,
}

impl Spinner {
    fn to_other(&self) -> another_crate::Spinner {
        match *self {
            Spinner::One => another_crate::Spinner::One,
            Spinner::Two => another_crate::Spinner::Two,
            Spinner::Three => another_crate::Spinner::Three,
        }
    }
}

impl Distribution<Spinner> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Spinner {
        match rng.gen_range(0, 3) {
            0 => Spinner::One,
            1 => Spinner::Two,
            _ => Spinner::Three,
        }
    }
}

fn main() {
    let spinner = rand::random::<Spinner>().to_other();
    println!("{:?}", spinner);
}

您可以计算微调器的数量并进行匹配:

extern crate rand;

use rand::Rng;

#[derive(Debug)]
enum Spinner {
    One,
    Two,
    Three,
}

fn rando<R: Rng>(mut rng: R) -> Spinner {
    match rng.gen_range(0, 3) {
        0 => Spinner::One,
        1 => Spinner::Two,
        _ => Spinner::Three,
    }
}

fn main() {
    let mut rng = rand::thread_rng();
    let spinner = rando(&mut rng);
    println!("{:?}", spinner);
}

您可以实现 newtype 并为其实现随机生成:

extern crate rand;

use rand::{distributions::Standard, prelude::*};

#[derive(Debug)]
enum Spinner {
    One,
    Two,
    Three,
}

struct RandoSpinner(Spinner);

impl Distribution<RandoSpinner> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> RandoSpinner {
        RandoSpinner(match rng.gen_range(0, 3) {
            0 => Spinner::One,
            1 => Spinner::Two,
            _ => Spinner::Three,
        })
    }
}

fn main() {
    let RandoSpinner(spinner) = rand::random();
    println!("{:?}", spinner);
}

另见:

答案 1 :(得分:2)

Shepmaster asked以来,我可以提出另外两种选择。

不幸的是rng.choose(Spinners)无法工作,因为没有办法迭代枚举值;见:In Rust, is there a way to iterate through the values of an enum?

您可以使用strum EnumIter来允许迭代。在Rand 0.4和0.5中,choose不支持迭代器,但您可以将所有选项收集到Vec中,也可以枚举并匹配索引。在Rand 0.6中,将有一个choose支持迭代器的变体,虽然它可能会很慢(取决于我们是否可以为ExactSizeIterator s优化它。)

use rand::prelude::*;

#[derive(EnumIter)]
enum Spinner { ... }

let mut rng = thread_rng();

let options = Spinner::iter().collect::<Vec<_>>();
let choice = rng.choose(&options);

// or:
let index = rng.gen_range(0, MAX);
let choice = Spinner::iter().enumerate().filter(|(i, _)| i == index).map(|(_, x)| x).next().unwrap();

// with Rand 0.6, though this may be slow:
let choice = Spinner::iter().choose(&mut rng);
// collecting may be faster; in Rand 0.6 this becomes:
let choice = Spinner::iter().collect::<Vec<_>>().choose(&mut rng);

另一种选择是将FromPrimitive trait num-derive与{{3}}一起使用:

#[derive(FromPrimitive)]
enum Spinner { ... }

let choice = Spinner::from_u32(rng.gen_range(0, MAX)).unwrap();