2016-08-23 8 views
2

比較できる複数のメンバーを持つ構造体、またはsort_byコールバックの場合は、データを優先順位と比較することがよくあります。最初に非等価な結果を返す最も簡単な方法は何ですか?

// Example of sorting a: Vec<[f64; 2]>, sort first by y, then x, 
xy_coords.sort_by(
    |co_a, co_b| { 
     let ord = co_a[1].cmp(&co_b[1]); 
     if ord != std::cmp::Ordering::Equal { 
      ord 
     } else { 
      co_a[0].cmp(&co_b[0]) 
     } 
    } 
); 

のみ最初の非同等の結果が返された複数のcmp機能を実行するために、より簡単な方法はありますか?

答えて

6

は、最初の非等しい結果Ordはタプルのために定義される方法は基本的だつまり

返される複数のCMP機能を実行します。タプルにあなたのタイプを変換する関数を作成し、それらを比較します。それは一般的ですので

fn main() { 
    let mut xy_coords = vec![[1, 0], [-1, -1], [0, 1]]; 

    fn sort_key(coord: &[i32; 2]) -> (i32, i32) { 
     (coord[1], coord[0]) 
    } 

    xy_coords.sort_by(|a, b| { 
     sort_key(a).cmp(&sort_key(b)) 
    }); 
} 

は、ちょうどそれのための方法があります:それはfloating point doesn't implement Ordので、あなたのケースを助けにはなりません

xy_coords.sort_by_key(sort_key); 

多くの可能性の一つはNaN上のプログラムを殺すためにある:

xy_coords.sort_by(|a, b| { 
    sort_key(a).partial_cmp(&sort_key(b)).expect("Don't know how to handle NaN") 
}); 

も参照してください


大きなタプルを作成して値を比較したくない場合があります。優先度の高い値が早期に終了するため無視されます。 Guava's ComparisonChainからページを盗む

、私たちは、私たちは余分な作業を避けるために、クロージャを使用することができます小さなビルダーを行うことができます。

use std::cmp::Ordering; 

struct OrdBuilder<T> { 
    a: T, 
    b: T, 
    ordering: Ordering, 
} 

impl<T> OrdBuilder<T> { 
    fn new(a: T, b: T) -> OrdBuilder<T> { 
     OrdBuilder { 
      a: a, 
      b: b, 
      ordering: Ordering::Equal, 
     } 
    } 

    fn compare_with<F, V>(mut self, mut f: F) -> OrdBuilder<T> 
     where F: for <'a> FnMut(&'a T) -> V, 
       V: Ord, 
    { 
     if self.ordering == Ordering::Equal { 
      self.ordering = f(&self.a).cmp(&f(&self.b)); 
     } 
     self 
    } 

    fn finish(self) -> Ordering { 
     self.ordering 
    } 
} 

これは、この答えは

struct Thing { 
    a: u8, 
} 

impl Thing { 
    fn b(&self) -> u8 { 
     println!("I'm slow!"); 
     42 
    } 
} 

fn main() { 
    let a = Thing { a: 0 }; 
    let b = Thing { a: 1 }; 

    let res = OrdBuilder::new(&a, &b) 
     .compare_with(|x| x.a) 
     .compare_with(|x| x.b()) 
     .finish(); 

    println!("{:?}", res); 
} 
+0

ように使用することができます質問の例には良いが、質問には直接言及していない。優先度の高い値が早期に終了するため、値を比較するために大きなタプルを作成したくない場合があります。基本的には、比較のためにタプルを作成することを避けることは合理的です。 – ideasman42

+0

@ ideasman42 updated – Shepmaster

+0

@ ideasman42 *は質問に直接言及しません* - もし*質問が**ある種の種類のコメントを掲載していたり​​、物事を作るのを避けていれば本当かもしれません。 **質問**はかなり明確でした:*最初の非等価な結果を生み出す比較をチェーン化する最も簡単な方法は何ですか?それはかなり直接的に答えられました。最初にあなたにとって重要だったことを正しく述べていないからといって、質問の精神を遡及的に変えることはできません。 – Shepmaster

関連する問題