与 Rust 勾心斗角 · 泛得彻底点儿

garfileo
English

在上一篇里,定义了一个网格结构

struct Mesh {
    points: Vec<Vec<f64>>,  // 点表
    facets: Vec<Vec<usize>> // 面表
}

有什么理由要求 Meshpoints 里的各顶点坐标必须是 f64 类型呢?

没有理由,所以 Mesh 结构应该定义为

struct Mesh<T> {
    n: usize, // 维度
    points: Vec<Vec<T>>,  // 点表
    facets: Vec<Vec<usize>> // 面表
}

请注意,我还为 Mesh 增加了维度信息。至于 facets,由于它存储的是顶点在 points 里的下标,有理由要求它是 usize 类型,因为 Vec 的下标就是 usize 类型。

一个向量,其元素为向量,该向量便是矩阵,因此 Meshpoints 成员实际上是矩阵,同理,Meshfacets 成员也是矩阵,故而在上一篇里,定义了泛型函数 matrix_fmt 将二者转化为字符串(String 类型)。现在,由于 Mesh 的定义发生了变化,matrix_fmt 也要相应有所变化,借此可再略微温习一遍泛型。

首先,写出一个也许并不正确的 matrix_fmt

struct Prefix<T> {
    status: bool,
    body: fn(&T) -> String
}

impl<T> Prefix<T> {
    fn new() -> Prefix<T> {
        Prefix{status: false, body: |_| "".to_string()}
    }
}

fn matrix_fmt<T>(v: &Vec<T>, prefix: Prefix<T>) -> String {
    let mut s = String::new();
    for x in v {
        let n = x.len();
        if prefix.status {
            s += (prefix.body)(x).as_str();
        }
        for i in 0 .. n {
            if i == n - 1 {
                s += format!("{}\n", x[i]).as_str();
            } else {
                s += format!("{} ", x[i]).as_str();
            }
        }
    }
    return s;
}

经过 rustc 的一番调教,matrix_fmt 最终会变成

fn matrix_fmt<T: Length + Index<usize>>(v: &Vec<T>,
                                        prefix: Prefix<T>) -> String
where <T as Index<usize>>::Output: fmt::Display,
      <T as Index<usize>>::Output: Sized {
    let mut s = String::new();
    for x in v {
        let n = x.len();
        if prefix.status {
            s += (prefix.body)(x).as_str();
        }
        for i in 0 .. n {
            if i == n - 1 {
                s += format!("{}\n", x[i]).as_str();
            } else {
                s += format!("{} ", x[i]).as_str();
            }
        }
    }
    return s;
}

之所以为 T 增加 fmt::Display 约束,是因为代码中 format! 的参数是 T。之所以为 T 增加 Sized 约束,是因为 rustc 希望能够在编译期间确定 T 的实例占用多少字节的空间。

基于 matrix_fmt 便可为 Mesh 实现 Display Trait:

impl<T: fmt::Display> fmt::Display for Mesh<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut info = String::new();
        info += format!("OFF\n").as_str();
        info += format!("{0} {1} 0\n", self.points.len(), self.facets.len()).as_str();
        info += matrix_fmt(&self.points,  Prefix::new()).as_str();
        info += matrix_fmt(&self.facets, Prefix{status: true,
                                                body: |x| format!("{} ", x.len())}).as_str();
        write!(f, "{}", info)
    }
}

完整的代码如下:

use std::fmt;
use std::path::Path;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::str::FromStr;
use std::num::ParseFloatError;
use std::ops::Index;

struct Mesh<T> {
    n: usize, // 维度
    points: Vec<Vec<T>>,  // 点表
    facets: Vec<Vec<usize>> // 面表
}

impl<T: FromStr<Err = ParseFloatError>> Mesh<T> {
    fn new(n: usize) -> Mesh<T> {
        return Mesh {n: n, points: Vec::new(), facets: Vec::new()};
    }
    fn load(&mut self, path: &str) {
        let path = Path::new(path);
        let file = File::open(path).unwrap();
        let buf = BufReader::new(file);

        let mut lines_iter = buf.lines().map(|l| l.unwrap());
        assert_eq!(lines_iter.next(), Some(String::from("OFF")));
        let second_line = lines_iter.next().unwrap();
        let mut split = second_line.split_whitespace();
        let n_of_points: usize = split.next().unwrap().parse().unwrap();
        let n_of_facets: usize = split.next().unwrap().parse().unwrap();

        for _i in 0 .. n_of_points {
            let line = lines_iter.next().unwrap();
            let mut p: Vec<T> = Vec::new();
            for x in line.split_whitespace() {
                p.push(x.parse().unwrap());
            }
            self.points.push(p);
        }
        for _i in 0 .. n_of_facets {
            let line = lines_iter.next().unwrap();
            let mut f: Vec<usize> = Vec::new();
            let mut split = line.split_whitespace();
            let n:usize = split.next().unwrap().parse().unwrap();
            assert_eq!(n, self.n);
            for x in split {
                f.push(x.parse().unwrap());
            }
            assert_eq!(n, f.len());
            self.facets.push(f);        
        }
    }
}

trait Length {
    fn len(&self) -> usize;
}

impl<T> Length for Vec<T> {
    fn len(&self) -> usize {
        return self.len();
    }
}

struct Prefix<T> {
    status: bool,
    body: fn(&T) -> String
}

impl<T> Prefix<T> {
    fn new() -> Prefix<T> {
        Prefix{status: false, body: |_| "".to_string()}
    }
}

fn matrix_fmt<T: Length + Index<usize>>(v: &Vec<T>,
                                        prefix: Prefix<T>) -> String
where <T as Index<usize>>::Output: fmt::Display,
      <T as Index<usize>>::Output: Sized {
    let mut s = String::new();
    for x in v {
        let n = x.len();
        if prefix.status {
            s += (prefix.body)(x).as_str();
        }
        for i in 0 .. n {
            if i == n - 1 {
                s += format!("{}\n", x[i]).as_str();
            } else {
                s += format!("{} ", x[i]).as_str();
            }
        }
    }
    return s;
}

impl<T: fmt::Display> fmt::Display for Mesh<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut info = String::new();
        info += format!("OFF\n").as_str();
        info += format!("{0} {1} 0\n", self.points.len(), self.facets.len()).as_str();
        info += matrix_fmt(&self.points,  Prefix::new()).as_str();
        info += matrix_fmt(&self.facets, Prefix{status: true,
                                                body: |x| format!("{} ", x.len())}).as_str();
        write!(f, "{}", info)
    }
}

fn main() {
    let mut mesh: Mesh<f64> = Mesh::new(3);
    mesh.load("foo.off");
    print!("{}", mesh);
}
阅读 650

5.9k 声望
0 粉丝
0 条评论
5.9k 声望
1.9k 粉丝
宣传栏