2016-07-30 44 views
2

例如实施FN性状(呼叫操作者)我有一个简单分类器对于不同类型的参数

struct Clf { 
    x: f64 
} 

分类器返回0,如果所观察到的值小于x和1,如果大于x。

我现在想要为这个分类器实现调用操作符。但是,该函数应该能够将float或vector作为参数。在矢量的情况下,输出是0或1的矢量,其大小与输入矢量相同。它应该像这样工作

let c = Clf { x : 0 }; 
let v = vec![-1, 0.5, 1]; 
println!("{}", c(0.5));  // prints 1 
println!("{}", c(v));  // prints [0, 1, 1] 

我怎么能写

impl Fn for Clf{ 
    extern "rust-call" fn call ... 
    ... 
} 
在这种情况下

回答

2

你不行。

首先,明确实施Fn*特性系列是不稳定的,随时都可能发生变化,因此依赖它是个坏主意。

其次,更重要的是,Rust编译器只是不会让你调用一个值为Fn*实现不同的参数类型。它不能解决你想要它做的事情,因为通常没有办法让它发生。唯一的解决方法就是完全确定你想要调用的特征,但是在这一点上,你已经失去了这种方法的任何可能的人体工程学优势。

只是定义和实现自己的特质,而不是试图使用Fn*特征。我对这个问题采取了一些自由,以避免/解决可疑的问题。

struct Clf { 
    x: f64, 
} 

trait ClfExt<T: ?Sized> { 
    type Result; 
    fn classify(&self, arg: &T) -> Self::Result; 
} 

impl ClfExt<f64> for Clf { 
    type Result = bool; 
    fn classify(&self, arg: &f64) -> Self::Result { 
     *arg > self.x 
    } 
} 

impl ClfExt<[f64]> for Clf { 
    type Result = Vec<bool>; 
    fn classify(&self, arg: &[f64]) -> Self::Result { 
     arg.iter() 
      .map(|v| self.classify(v)) 
      .collect() 
    } 
} 

fn main() { 
    let c = Clf { x : 0.0 }; 
    let v = vec![-1.0, 0.5, 1.0]; 
    println!("{}", c.classify(&0.5f64)); 
    println!("{:?}", c.classify(&v[..])); 
} 

:包括为完整起见; 实际上并没有这样做。不仅它不被支持,它的该死的丑陋。

#![feature(fn_traits, unboxed_closures)] 

#[derive(Copy, Clone)] 
struct Clf { 
    x: f64, 
} 

impl FnOnce<(f64,)> for Clf { 
    type Output = bool; 
    extern "rust-call" fn call_once(self, args: (f64,)) -> Self::Output { 
     args.0 > self.x 
    } 
} 

impl<'a> FnOnce<(&'a [f64],)> for Clf { 
    type Output = Vec<bool>; 
    extern "rust-call" fn call_once(self, args: (&'a [f64],)) -> Self::Output { 
     args.0.iter().cloned() 
      .map(|v| { FnOnce::call_once(self, (v,)) }) 
      .collect() 
    } 
} 

fn main() { 
    let c = Clf { x : 0.0 }; 
    let v = vec![-1.0, 0.5, 1.0]; 
    println!("{}", FnOnce::call_once(c, (0.5f64,))); 
    println!("{:?}", FnOnce::call_once(c, (&v[..],))); 
} 
2

简短的回答是:你不能。至少它不会按照你想要的方式工作。我认为最好的展示方式是查看会发生什么,但总体思路是Rust不支持函数重载。

对于此示例,我们将实施FnOnce,因为Fn要求FnMut,这需要FnOnce。所以,如果我们要把这个全部排序,我们可以为其他功能特性做到。

首先,这是不稳定的,所以我们需要一些功能标志

#![feature(unboxed_closures, fn_traits)] 

然后,我们采取的f64impl

impl FnOnce<(f64,)> for Clf { 
    type Output = i32; 
    extern "rust-call" fn call_once(self, args: (f64,)) -> i32 { 
     if args.0 > self.x { 
      1 
     } else { 
      0 
     } 
    } 
} 

的参数为Fn家庭性状的通过元组提供,所以这是(f64,)语法;这是一个只有一个元素的元组。

这一切都很好,我们现在可以做c(0.5),虽然它会消耗c,直到我们实现其他特征。

现在让我们做同样的事情Vec S:

impl FnOnce<(Vec<f64>,)> for Clf { 
    type Output = Vec<i32>; 
    extern "rust-call" fn call_once(self, args: (Vec<f64>,)) -> Vec<i32> { 
     args.0.iter().map(|&f| if f > self.x { 1 } else { 0 }).collect() 
    } 
} 

现在,我们有一个问题。如果我们尝试c(v)甚至c(0.5)(之前有效),我们会得到关于函数类型未知的错误。基本上,Rust不支持函数重载。但是我们仍然可以使用ufcs来调用函数,其中c(0.5)变为FnOnce::call_once(c, (0.5,))


不知道你的大局观,我想简单地通过给像这样Clf两个函数来解决这个问题:

impl Clf { 
    fn classify(&self, val: f64) -> u32 { 
     if val > self.x { 1 } else { 0 } 
    } 

    fn classify_vec(&self, vals: Vec<f64>) -> Vec<u32> { 
     vals.map(|v| self.classify(v)).collect() 
    } 
} 

则您在使用例如成为

let c = Clf { x : 0 }; 
let v = vec![-1, 0.5, 1]; 
println!("{}", c.classify(0.5));  // prints 1 
println!("{}", c.classify_vec(v));  // prints [0, 1, 1] 

我真的想要使第二个功能classify_slice,并采取&[f64]是一个更普遍的,那么你仍然可以使用它与vecs通过引用他们:c.classify_slice(&v)

4

这确实是可能的,但你需要一个新的特点和一堆烂摊子。

如果你开始与抽象

enum VecOrScalar<T> { 
    Scalar(T), 
    Vector(Vec<T>), 
} 

use VecOrScalar::*; 

你想要的方式,使用类型转换

T  (hidden) -> VecOrScalar<T> -> T  (known) 
Vec<T> (hidden) -> VecOrScalar<T> -> Vec<T> (known) 

因为这样你可以采取“隐蔽”型T,在VecOrScalar把它包并用match提取真实类型T

你也想

T  (known) -> bool  = T::Output 
Vec<T> (known) -> Vec<bool> = Vec<T>::Output 

但没有HKT这是一个有点棘手。相反,你可以做

T  (known) -> VecOrScalar<T> -> T::Output 
Vec<T> (known) -> VecOrScalar<T> -> Vec<T>::Output 

如果你允许一个可以恐慌的分支。

这种特点将因此是

trait FromVecOrScalar<T> { 
    fn put(self) -> VecOrScalar<T>; 

    type Output; 
    fn get(out: VecOrScalar<bool>) -> Self::Output; 
} 

与实施方式

impl<T> FromVecOrScalar<T> for T { 
    fn put(self) -> VecOrScalar<T> { 
     Scalar(self) 
    } 

    type Output = bool; 
    fn get(out: VecOrScalar<bool>) -> Self::Output { 
     match out { 
      Scalar(val) => val, 
      Vector(_) => panic!("Wrong output type!"), 
     } 
    } 
} 
impl<T> FromVecOrScalar<T> for Vec<T> { 
    fn put(self) -> VecOrScalar<T> { 
     Vector(self) 
    } 

    type Output = Vec<bool>; 
    fn get(out: VecOrScalar<bool>) -> Self::Output { 
     match out { 
      Vector(val) => val, 
      Scalar(_) => panic!("Wrong output type!"), 
     } 
    } 
} 

你的类

#[derive(Copy, Clone)] 
struct Clf { 
    x: f64, 
} 

将首先执行两个B牧场:

impl Clf { 
    fn calc_scalar(self, f: f64) -> bool { 
     f > self.x 
    } 

    fn calc_vector(self, v: Vec<f64>) -> Vec<bool> { 
     v.into_iter().map(|x| self.calc_scalar(x)).collect() 
    } 
} 

然后,它会为T: FromVecOrScalar<f64>

impl<T> FnOnce<(T,)> for Clf 
    where T: FromVecOrScalar<f64> 
{ 

通过实施FnOnce派遣与类型

type Output = T::Output; 
    extern "rust-call" fn call_once(self, (arg,): (T,)) -> T::Output { 

调度第一格的私有类型,所以你可以用解压enum,然后T::get的结果,再次隐藏它。

 match arg.put() { 
      Scalar(scalar) => 
       T::get(Scalar(self.calc_scalar(scalar))), 
      Vector(vector) => 
       T::get(Vector(self.calc_vector(vector))), 
     } 
    } 
} 

然后,成功:

fn main() { 
    let c = Clf { x : 0.0 }; 
    let v = vec![-1.0, 0.5, 1.0]; 
    println!("{}", c(0.5f64)); 
    println!("{:?}", c(v)); 
} 

由于编译器可以通过这一切说大话的看到,它实际上完全编译客场基本上是相同的组件直接调用calc_方法。

但是,这并不是说这是很好的写作。这样的超载是一种痛苦,脆弱,当然也是一个坏主意™。不要这样做,尽管知道你可以。

+0

非常感谢! – asdetrefle

相关问题