如何获得多维数组的尺寸?

时间:2016-12-20 22:15:40

标签: rust

我想获得Rust中数组的所有维度的大小,但我不确定如何解决这个问题。我能够使用x.len()获取数组的长度,但我需要以某种方式递归地执行此操作。 我希望能够做到这样的事情:

let x = [[1, 2, 3], [4, 5, 6]];
println!("{:?}", x.dimensions());
// [2, 3]

形状类似[[1], [2, 3], [4, 5, 6]]的切片应该会出错。

2 个答案:

答案 0 :(得分:3)

对于每种可能的嵌套深度,都不可能以通用方式执行此操作。 Rust是一种静态类型语言,因此您必须知道输入和输出类型。 [1]的输入类型是什么?[[1]]的输入类型是什么?同样,相应的输出类型是什么?

我最接近的是具有相关类型的特征。这允许为特定类型实现它,然后关联另一种输出类型:

trait Thing {
    type Dimensions;
    fn thing(self) -> Self::Dimensions;
}

但是,一旦实施它,就会遇到问题:

impl<'a, T> Thing for &'a[T] {
    type Dimensions = usize;

    fn thing(self) -> usize { 
        self.len() 
    }
}

impl<'a, T> Thing for &'a[&'a[T]] {
    type Dimensions = [usize; 2];

    fn thing(self) -> Self::Dimensions {
        [self.len(), self[0].len()]
    }
}
error[E0119]: conflicting implementations of trait `Thing` for type `&[&[_]]`:
  --> src/main.rs:14:1
   |
6  | impl<'a, T> Thing for &'a[T] {
   | - first implementation here
...
14 | impl<'a, T> Thing for &'a[&'a[T]] {
   | ^ conflicting implementation for `&[&[_]]`

这是因为&[[T]] &[T]

你也可以考虑尝试一些递归的东西,但是没有办法说&[T]并且知道T是否可以进一步迭代。如果您具有HasLength特征和DoesntHaveLength特征,则不会阻止您实现单个类型的两个特征。因此,你再次被阻止。

以下是使用专业化的部分尝试:

#![feature(specialization)]

trait Dimensions: Sized {
    fn dimensions(self) -> Vec<usize> {
        let mut answers = vec![];
        self.dimensions_core(&mut answers);
        answers
    }
    fn dimensions_core(self, &mut Vec<usize>);
}

impl<'a, T> Dimensions for &'a [T] {
    default fn dimensions_core(self, answers: &mut Vec<usize>) {
        answers.push(self.len());
    }
}

impl<'a, T> Dimensions for &'a [T]
    where T: Dimensions + Copy
{
    fn dimensions_core(self, answers: &mut Vec<usize>)  {
        answers.push(self.len());
        self[0].dimensions_core(answers);
    }
}

impl<'a, T> Dimensions for [T; 2] {
    default fn dimensions_core(self, answers: &mut Vec<usize>)  {
        answers.push(2)
    }
}

impl<'a, T> Dimensions for [T; 2] 
    where T: Dimensions + Copy
{
    fn dimensions_core(self, answers: &mut Vec<usize>)  {
        answers.push(2);
        self[0].dimensions_core(answers);
    }
}

impl<'a, T> Dimensions for [T; 3] {
    default fn dimensions_core(self, answers: &mut Vec<usize>)  {
        answers.push(3)
    }
}

impl<'a, T> Dimensions for [T; 3] 
    where T: Dimensions + Copy
{
    fn dimensions_core(self, answers: &mut Vec<usize>)  {
        answers.push(3);
        self[0].dimensions_core(answers);
    }
}

// Also implement for all the other sizes of array as well as `Vec`

fn main() {
    let x = [[1, 2, 3], [4, 5, 6]];
    println!("{:?}", x.dimensions());

    let x = [[1, 2], [3, 4], [5, 6]];
    println!("{:?}", x.dimensions());
}

它有一个明显的缺点,你仍然需要为每个数组大小实现特征,以便获得专业化。

我猜你是来自一种高度动态的语言。不同的语言有不同的优点和缺点。在Rust中,您知道您的输入类型,因此该函数无法知道我的类型的嵌套。如果它会收到Vec<T>Vec<&[Vec<T>]>,我会提前知道嵌套的深度,所以我可以编写一个返回每个嵌套长度的函数:

fn depth3<A, B, C, T>(a: A) -> [usize; 3]
    where A: AsRef<[B]>,
          B: AsRef<[C]>,
          C: AsRef<[T]>
{
    let a = a.as_ref();
    // All of these should check that the length is > 1
    // and possibly that all children have same length
    let b = a[0].as_ref();
    let c = b[0].as_ref();
    [a.len(), b.len(), c.len()] 
}

fn main() {
    let x = [[[1], [2], [3]], [[4], [5], [6]]];
    println!("{:?}", depth3(&x));
}

这个函数和我想的一样通用 - 你传递对数组,切片,向量或这些类型的直接值的引用。事实上,我想不出一种方法甚至可以定义具有未知深度的切片/矢量/数组。我想要做一些类似的事情你必须引入一些带有间接性的新类型(可能是一个枚举),这样你就可以拥有一个非无限大小。

答案 1 :(得分:1)

数组定义为[T]T不能同时为[U; 2][U; 3]。这意味着您甚至无法通过此编译过程。

如果您使用Vec<Vec<T>>作为@Shepmaster提示,则可以执行以下操作。

fn main() {
    let x = vec![vec![1, 2, 3], vec![4, 5]];
    println!("{:?}", get_2d_dimension(&x));
}

fn get_2d_dimension<T>(arr: &[Vec<T>]) -> Result<(usize, usize), &str> {
    let rows = arr.len();
    if rows <= 1 {
        return Err("Not 2d");
    }
    let cols = arr[0].len();
    if arr.iter().skip(1).filter(|v| v.len() == cols).count() != rows - 1 {
        Err("Not square.")
    } else {
        Ok((rows, cols))
    }
}