2016-07-30 11 views
2

の異なるタイプののFn形質を(オペレータを呼び出す)実装は0を返します。Iは、単純な分類器</p> <pre><code>struct Clf { x: f64 } </code></pre> <p>クラシファイアを有する。例えば、引数

私は今、この分類器のための呼び出し演算子を実装したいです。ただし、関数は引数として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*ファミリの特性を明示的に実装することは不安定であり、いつでも変更される可能性があるため、それに依存するのは悪い考えです。第二に

、そしてもっと重要なのは、錆コンパイラはちょうどはあなたが別の引数の型について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[..])); 
} 

:完全を期すために含ま。 これは実際には行いません。サポートされていないだけでなく、ダン uglyです。

#![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

短い答えは次のとおりです。少なくともそれはあなたが望むように動作しません。私はそれを見て、何が起こるかを見極める最善の方法だと思うが、一般的な考え方は、関数のオーバーロードをサポートしていないということである。

FnにはFnMutが必要であり、FnOnceが必要なため、この例ではFnOnceを実装します。だから、もしこれをすべてソートするならば、他の関数の特性のためにそれを行うことができます。

まず、これは不安定であるので、我々は次に

#![feature(unboxed_closures, fn_traits)] 

いくつかの機能フラグを必要とする、のがf64を取るためimplをやらせる:

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,)の構文です。ただ1つの要素を持つタプルです。

他の特性を実装するまで、を使用することがありますが、これはすべてを消費しますが、これはすべて問題なく完了しました。

今度はVec秒間同じことをやらせる:

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] 

なり2番目の関数classify_sliceを作成して&[f64]をもう少し一般的なものにしたい場合は、それらを参照してvecsで引き続き使用することができます:c.classify_slice(&v)

4

これは確かに可能ですが、あなたは新しい特性と混乱を必要とします。

あなたは抽象

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

use VecOrScalar::*; 

で起動した場合あなたは、あなたがVecOrScalarでそれをラップし、「隠された」タイプTを取ることができるので、型変換

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

を使用するための方法をしたいです実数型Tmatchで抽出します。

また

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 

パニックする可能性のあるブランチを許可する場合。

形質は、このようにクラス

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

が最初の2件のBを実装する実装

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!"), 
     } 
    } 
} 

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

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

あろう牧場:

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() 
    } 
} 

あなたがそれを取り出すことができますので、それは、種類

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

派遣最初のボックスのプライベートタイプのアップとT: FromVecOrScalar<f64>

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

ためFnOnceを実装することで、派遣しますenumを入力し、次にT::getを入力して再度非表示にします。

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

その後、成功:コンパイラはこのmalarkyのすべてを見ることができるので

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_メソッドを直接呼び出すと基本的に同じアセンブリに完全に離れてコンパイルされます。

しかし、それは書くのがいいとは言いません。このようなオーバーロードは、痛い、壊れやすい、そして確かにBad Idea™です。あなたができることを知っても大丈夫ですが、それをしないでください。

+0

ありがとうございます。 – asdetrefle

関連する問題

 関連する問題