簡體   English   中英

通用 function 超過 collections 個號碼

[英]Generic function over collections of numbers

我正在嘗試使用assert_approx_eq! 比較結構的元素! 宏。 我正在研究的結構是

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Matrix3D {
    n: [[f64; 3]; 3],
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vector3D {
    pub x: f64,
    pub y: f64,
    pub z: f64,
}

我已經在單獨的迭代器結構的幫助下為這兩種類型實現了IntoIterator特性

impl IntoIterator for Matrix3D {
    type Item = f64;
    type IntoIter = Matrix3DIterator;

    fn into_iter(self) -> Self::IntoIter {
        Matrix3DIterator {
            n: self.n,
            index: 0,
        }
    }
}

pub struct Matrix3DIterator {
    n: [[f64; 3]; 3],
    index: usize,
}

impl Iterator for Matrix3DIterator {
    type Item = f64;

    fn next(&mut self) -> Option<Self::Item> {
        let i = self.index / 3;
        let j = self.index % 3;
        if i < 3 && j < 3 {
            self.index += 1;
            Some(self.n[j][i])
        } else {
            None
        }
    }
}
impl IntoIterator for Vector3D {
    type Item = f64;
    type IntoIter = Vector3DIterator;

    fn into_iter(self) -> Self::IntoIter {
        Vector3DIterator { x: self.x, y: self.y, z: self.z, index: 0}
    }
}

pub struct Vector3DIterator {
    x: f64,
    y: f64,
    z: f64,
    index: usize,
}

impl Iterator for Vector3DIterator {
    type Item = f64;

    fn next(&mut self) -> Option<Self::Item> {
        let result = match self.index {
            0 => Some(self.x),
            1 => Some(self.y),
            2 => Some(self.z),
            _ => None
        };
       self.index += 1; 
       result
    }
}

在為這些結構編寫測試時,我制作了一個助手 function,它遍歷它們並比較每個元素。 我很難表達這是一種通用的方式,這樣我就可以使用相同的 function 來比較兩個矩陣或兩個向量。 具體來說,集合的類型必須支持減法運算,並為assert_approx_eq!實現一個abs assert_approx_eq! 宏。 到目前為止我得到的是

use num_traits::Float;
fn elementwise_approx_comparison<I: IntoIterator>(result: I, expected: I) -> () 
    where I::Item: Float,
          I::Item: std::fmt::Debug {
        for (r, e) in std::iter::zip(result, expected) {
            assert_approx_eq!(r, e);
        }
    }

這給出了以下錯誤

error[E0308]: mismatched types
   --> src/matrix.rs:411:13
    |
411 |             assert_approx_eq!(r, e);
    |             ^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found floating-point number
    |
    = note: expected associated type `<I as IntoIterator>::Item`
                          found type `{float}`
    = note: this error originates in the macro `assert_approx_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider constraining the associated type `<I as IntoIterator>::Item` to `{float}`
    |
407 |     fn elementwise_approx_comparison<I: IntoIterator<Item = {float}>>(result: I, expected: I) -> () 
    |                                                     ++++++++++++++++

編譯器期望關聯類型卻發現浮點數是什么意思? 我如何表達 function elementwise_approx_comparison應該接受一個集合I可以做成一個迭代器,並迭代(在這種情況下)浮動類型數字?

問題是assert_approx_eq試圖比較兩個值和一個文字常量之間的差異,但是 Rust 不知道如何將這個文字常量的類型與值的實際類型統一。 但是assert_approx_eq允許您指定比較閾值,而Float定義了一個epsilon()方法,它為您提供適合這種用途的“小正值”。 因此你可以這樣做:

use num_traits::Float;
fn elementwise_approx_comparison<I: IntoIterator>(result: I, expected: I) -> () 
    where I::Item: Float,
          I::Item: std::fmt::Debug 
{
    for (r, e) in std::iter::zip(result, expected) {
        assert_approx_eq!(r, e, Float::epsilon());
    }
}

或者如果f32::EPSILON對你來說太小,你可以使用Float::from自己指定閾值:

use num_traits::Float;
fn elementwise_approx_comparison<I: IntoIterator>(result: I, expected: I) -> () 
    where I::Item: Float,
          I::Item: std::fmt::Debug 
{
    for (r, e) in std::iter::zip(result, expected) {
        assert_approx_eq!(r, e, Float::from(1e-6f32));
    }
}

這是因為宏在定義中使用了浮點數。 您還可以要求該類型與 f64 相當。 然而,這有效地將類型限制為 f64,這並不理想。

fn elementwise_approx_comparison<I: IntoIterator>(result: I, expected: I) -> ()
where
    <I as IntoIterator>::Item: Float + PartialOrd<f64>,
    I::Item: std::fmt::Debug,
{
    for (r, e) in std::iter::zip(result, expected) {
        assert_approx_eq!(r, e);
    }
}

另一種解決方案是要求調用者為 function 構造 epsilon。

fn elementwise_approx_comparison<I: IntoIterator<Item = F>, F: Float>(
    result: I,
    expected: I,
    eps: F,
) -> ()
where
    I::Item: std::fmt::Debug,
{
    for (r, e) in std::iter::zip(result, expected) {
        assert_approx_eq!(r, e, eps);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM