#TL;DR
i32にもf32にもmaxを使うような関数max(a:T, b:T)を作るときは,ジェネリック境界にPartialOrdを使いましょう.
max2dをジェネリックにしたい
2次元の行列からmaxを計算する関数があったとしましょう.
use ndarray::*;
use std::f32;
pub fn max2d_f32(input: &Array2<f32>) -> Array1<f32>
{
input.fold_axis(Axis(1), f32::MIN, |m, i| (*m).max(*i))
}
これをi32でも動くようにしたい.
pub fn max2d_i32(input: &Array2<i32>) -> Array1<i32>
{
input.fold_axis(Axis(1), i32::MIN, |m, i| (*m).max(*i))
}
これだと,使うとき分岐しなきゃいけないし,f64にも対応したいってときに関数が増えていく.
そこで,ジェネリックにするために
use ndarray::*;
use num_traits::bounds::Bounded;
fn max<T:PartialOrd>(a:T, b:T)-> T { if a > b {a} else{b}}
pub fn max2d<T>(input: &Array2<T>) -> Array1<T>
where T: Clone + Copy + Bounded + PartialOrd
{
input.fold_axis(Axis(1), T::min_value(), |m, i| max(*m, *i))
}
と書けばOK.
maxはTがOrdという前提なんだけど,f32はNanがあるためにPartialOrd,という点に注意して書けば,
大丈夫!
動作確認用
use ndarray::*;
use ndarray_rand::RandomExt;
use ndarray_rand::rand_distr::Uniform;
use std::f32;
use std::i32;
use num_traits::bounds::Bounded;
pub fn max2d_f32(input: &Array2<f32>) -> Array1<f32>
{
input.fold_axis(Axis(1), f32::MIN, |m, i| (*m).max(*i))
}
pub fn max2d_i32(input: &Array2<i32>) -> Array1<i32>
{
input.fold_axis(Axis(1), i32::MIN, |m, i| (*m).max(*i))
}
fn max<T:PartialOrd>(a:T, b:T)-> T { if a > b {a} else{b}}
pub fn max2d<T>(input: &Array2<T>) -> Array1<T>
where T: Clone + Copy + Bounded + PartialOrd
{
input.fold_axis(Axis(1), T::min_value(), |m, i| max(*m, *i))
}
fn main() {
let a = Array::random((10000, 1000), Uniform::new(0., 10.));
let b = Array::random((10000, 1000), Uniform::new(0, 100));
println!("{:8.4}", max2d_f32(&a));
println!("{:8.4}", max2d(&a));
println!("{:8.4}", max2d_i32(&b));
println!("{:8.4}", max2d(&b));
}
必要なジェネリック境界が減ると,もっとスッキリするんだけど.