温馨提示:本文翻译自stackoverflow.com,查看原文请点击:rust - How to select a random enum value with some limitations
enums rust

rust - 如何选择有一些限制的随机枚举值

发布于 2020-04-10 00:20:03

语境

  • 对象在一个方向移动:上,右,下,左
  • 下一个方向将是随机选择的。
  • 下一个可能的方向不能向后。

例如:

如果向右移动,则可以向上,向右向下移动,但不能向左移动。

当前方向和可能的下一个方向

枚举

有一个简单的方向指示枚举:

#[derive(Debug, PartialEq)]
enum Direction {
  Up,
  Right,
  Down,
  Left,
}

功能签名

我会说这是完成任务的功能签名:

fn next_direction(current_dir: Direction) -> Direction

当前实施

这是我当前的实现:

use rand::prelude::*;

fn next_direction(current_dir: Direction) -> Direction {
    let mut rng = thread_rng();
    // Possible next direction
    let next_dir_iter = [
        Direction::Up,
        Direction::Down,
        Direction::Left,
        Direction::Right,
    ]
    .iter()
    .filter(|&dir| match (current_dir, dir) {
        (Direction::Up, Direction::Down) => false,
        (Direction::Down, Direction::Up) => false,
        (Direction::Left, Direction::Right) => false,
        (Direction::Right, Direction::Left) => false,
        (_, _) => true,
    });
    // Choose one
    let dir = next_dir_iter.choose(&mut rng).unwrap();
    // Return Direction instead of &Direction
    match dir {
        Direction::Up => Direction::Up,
        Direction::Down => Direction::Down,
        Direction::Left => Direction::Left,
        Direction::Right => Direction::Right,
    }
}

可以用更清晰,更简单,更有效的方式编写此函数吗?

我会说可读性是一个加号,所以一个划线员或代码高尔夫实施不可能是最佳的。

我已经发现了一个相关的问题:如何从枚举中选择随机值?

谢谢 =)

查看更多

提问者
Ignacio Lago
被浏览
127
Boiethios 2020-02-01 22:09

您可以手动编写每种情况的可能指示:

use rand::prelude::*;
use Direction::*;

#[derive(Debug, PartialEq, Copy, Clone)]
enum Direction {
    Up,
    Right,
    Down,
    Left,
}

impl Direction {
    fn next_random(self) -> Self {
        match self {
            Up => [Up, Left, Right],
            Down => [Down, Left, Right],
            Left => [Up, Down, Left],
            Right => [Up, Down, Right],
        }
        .choose(&mut thread_rng())
        .copied()
        .unwrap()
    }
}

当然,如果您的枚举有很多变体,那么最好有一个更通用的解决方案:

impl Direction {
    fn all() -> &'static [Self] {
        &[Up, Down, Left, Right]
    }

    fn opposite(self) -> Self {
        match self {
            Up => Down,
            Down => Up,
            Left => Right,
            Right => Left,
        }
    }

    fn next_random(self) -> Self {
        let next = Self::all()
            .iter()
            .filter(|&&d| d != self.opposite())
            .choose(&mut thread_rng());

        *next.unwrap()
    }
}

请注意,如果您想要更好的性能或灵活性,则可以将随机数生成器作为参数传递:

fn next_random(self, rng: &mut impl Rng) -> Self {
    let next = Self::all()
        .iter()
        .filter(|&&d| d != self.opposite())
        .choose(rng);

    *next.unwrap()
}