如何在Rust上生成范围内的Point结构

3duebb1j  于 2023-01-17  发布在  其他
关注(0)|答案(2)|浏览(142)

我不明白如何编写trait来生成某个范围内的Point。
我尝试使用rand::Rng为Point结构体生成随机值(用于扫雷舰中的坐标)。
我使用这个网站Generate Random Values - Rust Cookbook,它很好地为简单的数据类型的范围工作。此外,与点工作的例子。
有人能提供代码的例子和一些关于统一点结构的特点的解释吗?

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

fn main() {
  let width: u8 = 15; let height: u8 = 15;
  let mine_count: u8 = 40;
  let mut rng = rand::thread_rng();
  let ranger = Uniform::from(0..width);
  for _i in 0..=mine_count{
    let rand_point: Point = ranger.sample(&mut rng);
    println!("Random Point: {:?}", rand_point);
    }
}

#[derive(Debug)]
struct Point {
    x: u8,
    y: u8,
}

impl Distribution<Point> for Uniform<Point> {
  fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
    let (rand_x, rand_y) = rng.gen();
    Point {
      x: rand_x,
      y: rand_y,
    }
  }
}
Compiling my-project v0.1.0 (/home/runner/SyntaxTest)
error[E0277]: the trait bound `Point: SampleUniform` is not satisfied
   --> src/main.rs:56:30
    |
56  | impl Distribution<Point> for Uniform<Point> {
    |                              ^^^^^^^^^^^^^^ the trait `SampleUniform` is not implemented for `Point`

note: required by a bound in `Uniform`
   --> /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/src/distributions/uniform.rs:179:23
    |
179 | pub struct Uniform<X: SampleUniform>(X::Sampler);
    |                       ^^^^^^^^^^^^^ required by this bound in `Uniform`

error[E0277]: the trait bound `Point: SampleUniform` is not satisfied
   --> src/main.rs:57:30
    |
57  |   fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
    |                              ^^^^^ the trait `SampleUniform` is not implemented for `Point`

note: required by a bound in `Uniform`
   --> /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/src/distributions/uniform.rs:179:23
    |
179 | pub struct Uniform<X: SampleUniform>(X::Sampler);
    |                       ^^^^^^^^^^^^^ required by this bound in `Uniform`
ncecgwcz

ncecgwcz1#

这里的问题是你的代码没有点分布的概念,如果你看Uniform结构体的签名,它的泛型必须实现SampleUniform特征,根据文档,SampleUniform特征定义了采样器类型,应该使用它来生成随机分布,所以,当你写这个:

impl Distribution<Point> for Uniform<Point> {
  fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
    let (rand_x, rand_y) = rng.gen();
    Point {
      x: rand_x,
      y: rand_y,
    }
  }
}

当调用Uniform::from(0..width)时,您同时也为u8值定义了一个采样器,这并不是您最终想要的,您想要的(根据代码判断)是生成一个min之间的点的随机分布,比如Point { x:0,y:0 }和一个最大值,最大值可以是另一个点,但也可以定义为一个范围、一个增量、一个区域等。我们的想法是能够编写如下内容:

let ranger = Uniform::from(
        Point::default()..Point {
            x: width,
            y: height,
        },
    );

其中Point::default()是原点。为了解决你的问题,你需要能够告诉兰德crate你要求的点的分布是什么意思。为了保持你的代码原样,你可以这样重写它:

use rand::distributions::uniform::{SampleUniform, UniformInt, UniformSampler};
use rand::distributions::{Distribution, Uniform};
use rand::Rng;

fn main() {
    let width: u8 = 4;
    let height: u8 = 5;
    let mine_count: u8 = 40;
    let mut rng = rand::thread_rng();
    let ranger = Uniform::from(
        Point::default()..Point {
            x: width,
            y: height,
        },
    );
    for _i in 0..=mine_count {
        let rand_point: Point = ranger.sample(&mut rng);
        println!("Random Point: {:?}", rand_point);
    }
}

#[derive(Clone, Copy, Debug, Default)]
struct Point {
    x: u8,
    y: u8,
}

struct UniformPoint {
    x: UniformInt<u8>,
    y: UniformInt<u8>,
}

impl SampleUniform for Point {
    type Sampler = UniformPoint;
}

impl UniformSampler for UniformPoint {
    type X = Point;

    fn new<B1, B2>(low: B1, high: B2) -> Self
    where
        B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
        B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
    {
        let low = *low.borrow();
        let high = *high.borrow();
        UniformPoint {
            x: UniformInt::<u8>::new_inclusive(low.x, high.x - 1),
            y: UniformInt::<u8>::new_inclusive(low.y, high.y - 1),
        }
    }

    fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
    where
        B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
        B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
    {
        let low = *low.borrow();
        let high = *high.borrow();
        UniformPoint {
            x: UniformInt::<u8>::new_inclusive(low.x, high.x),
            y: UniformInt::<u8>::new_inclusive(low.y, high.y),
        }
    }

    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
        Point {
            x: self.x.sample(rng),
            y: self.y.sample(rng),
        }
    }
}

impl Distribution<Point> for Point {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
        let (rand_x, rand_y) = rng.gen();
        Point {
            x: rand_x,
            y: rand_y,
        }
    }
}

注意,您不再需要实现Distribution trait,因为Uniform::from只要求您的结构体实现SampleUniform
这里的技巧是Point中的每个字段都需要从值的分布中生成,既然可以使用UniformInt::<u8>...来生成这些分布,那么您所需要的只是一个附属结构体,它可以作为这些分布的占位符。
注意,现在当你调用ranger.sample()时,你实际上是在做两个调用,一个调用x.sample,一个调用y. sample。
这将产生一个简洁的API,您可以像以前一样使用ranger而无需任何修改。另一个解决方案是保留u8分布并通过以下操作创建随机点:

for _i in 0..=mine_count {
        let rand_point = Point {
            x: ranger.sample(&mut rng),
            y: ranger.sample(&mut rng),
        }
        println!("Random Point: {:?}", rand_point);
    }

这样你就不需要实现任何东西,但是也不那么有趣。希望这能有所帮助。

eit6fx6z

eit6fx6z2#

您希望为Uniform<u8>而不是Uniform<Point>实现Distribution<Point>

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

fn main() {
  let width: u8 = 15; let height: u8 = 15;
  let mine_count: u8 = 40;
  let mut rng = rand::thread_rng();
  let ranger = Uniform::from(0..width);
  for _i in 0..=mine_count{
    let rand_point: Point = ranger.sample(&mut rng);
    println!("Random Point: {:?}", rand_point);
    }
}

#[derive(Debug)]
struct Point {
    x: u8,
    y: u8,
}

impl Distribution<Point> for Uniform<u8> {
  fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
    let (rand_x, rand_y) = rng.gen();
    Point {
      x: rand_x,
      y: rand_y,
    }
  }
}

Playground

相关问题