2016-07-30 43 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

相關問題