2016-03-04 5 views
11

私はi32を含む小さな構造体を持っている:比較項目が比較項目の一部ではないデータに依存する場合、どのようにOrdを実装できますか?

struct MyStruct { 
    value: i32, 
} 

私はBTreeMapMyStructを格納するためまたはその要素にOrdを持っているあなたを必要とする他のデータ構造にOrdを実装したいです。私の場合は

、それらに value Sに依存しない MyStructの2つのインスタンスを比較しますが、別のデータ構造(辞書)を求め、そのデータ構造は、私が作成されます BTreeMapのインスタンスごとに一意です。だから、理想的には、次のようになります。

impl Ord for MyStruct { 
    fn cmp(&self, other: &Self, dict: &Dictionary) -> Ordering { 
     dict.lookup(self.value).cmp(dict.lookup(other.value)) 
    } 
} 

Ord実装だけMyStructの2つのインスタンス、何よりもアクセスすることができますので、しかし、これは、できません。

解決策の1つはMyStructに辞書へのポインタを格納することですが、それは過剰です。 MyStructは単純なラッパーであり、ポインターはそのサイズを倍にします。別の解決方法は静的なグローバルを使用することですが、それは良い解決策ではありません。

C++のソリューションは簡単でしょう。ほとんどのSTLアルゴリズム/データ構造ではコンパレータを渡すことができます。コンパレータは、ある状態の関数オブジェクトになります。だから私はRustがこれに何とかマッチするイディオムを持っていると信じています、これを達成する方法はありますか?

+1

を実際には64ビット環境では、ポインタが 'MyStruct'のサイズを4倍になります32ビットから128ビット(64ポインタ、32値、32パディング)。 –

+0

あなたが実際に注文することに気をつけていますか、単に 'MyStruct'インスタンスをマップに配置しようとしていますか?後者の場合は、['HashMap'](https://doc.rust-lang.org/std/collections/struct.HashMap.html)を使用する方がはるかに意味があります – TheHansinator

+0

ああ、そうです質問のうち、注文が必要であると仮定します。私は時々最小値/最大値をポップする必要があるならば、同様の問題が 'BinaryHeap'で起こると思います。 – loudandclear

答えて

5

錆(より具体的には、錆のlibcollections)は現在、コンパレータのような構造を持っていないので、おそらく可変静的を使用するのが最善の策です。これはrustc内でも使用されます。 string internerは静的です。これは、ユースケースがまれではないので、私たちがそれを請願した場合、Rustはいつか外部コンパレータを取得します。

+0

言語仕様には使用できないものはありますか?データ構造体がそのコンストラクタ内の引数としてクロージャを受け取り、それを格納するならば、十分であるはずです。だから私は問題は "それは不可能"ではないと思う、それは単に標準ライブラリがそのように設計されていないということですか? – loudandclear

+2

正確です。あなたは 'std :: collections'をそのまま使うことはできません。 – llogiq

+0

libstdコレクションは、デフォルトの型パラメータを追加することで、これ以降のバージョンでこれをサポートすることができます。 – bluss

8

カスタムコンパレータを使用できるかどうかの議論は忘れずに、新しい(ラッピング)タイプを使用して同じ効果を得ることができるほとんどの場合、この複雑なAPIを多く使うことに決めましたそのためにPartialOrdを再定義してください。

究極的には、APIのシンプルさと特別なニーズ(これは多分外部リソースへのアクセスとしてまとめられる)とのトレードオフでした。あなたの特定のケースで


2つの解があります。

  • APIにそれが意図された方法を使用します。そして、MyStructのインスタンスと辞書への参照の両方を含むラッパー構造を作成するにはそのラッパーにOrdを定義し、APIを回避BTreeMap
  • にキーとしてこれを使用して...何とか

個人的には、APIを意図したとおりに使用することをアドバイスし、それを回避しようとする前に測定してください。


@ker親切コメント(playground version)にラッピングを達成する次の図を提供することであった:

#[derive(Eq, PartialEq, Debug)] 
struct MyStruct { 
    value: i32, 
} 

#[derive(Debug)] 
struct MyStructAsKey<'a> { 
    inner: MyStruct, 
    dict: &'a Dictionary, 
} 

impl<'a> Eq for MyStructAsKey<'a> {} 

impl<'a> PartialEq for MyStructAsKey<'a> { 
    fn eq(&self, other: &Self) -> bool { 
     self.inner == other.inner && self.dict as *const _ as usize == other.dict as *const _ as usize 
    } 
} 

impl<'a> Ord for MyStructAsKey<'a> { 
    fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { 
     self.dict.lookup(&self.inner).cmp(&other.dict.lookup(&other.inner)) 
    } 
} 

impl<'a> PartialOrd for MyStructAsKey<'a> { 
    fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { 
     Some(self.dict.lookup(&self.inner).cmp(&other.dict.lookup(&other.inner))) 
    } 
} 

#[derive(Default, Debug)] 
struct Dictionary(::std::cell::RefCell<::std::collections::HashMap<i32, u64>>); 

impl Dictionary { 
    fn ord_key<'a>(&'a self, ms: MyStruct) -> MyStructAsKey<'a> { 
     MyStructAsKey { 
      inner: ms, 
      dict: self, 
     } 
    } 
    fn lookup(&self, key: &MyStruct) -> u64 { 
     self.0.borrow()[&key.value] 
    } 
    fn create(&self, value: u64) -> MyStruct { 
     let mut map = self.0.borrow_mut(); 
     let n = map.len(); 
     assert!(n as i32 as usize == n); 
     let n = n as i32; 
     map.insert(n, value); 
     MyStruct { 
      value: n, 
     } 
    } 
} 

fn main() { 
    let dict = Dictionary::default(); 
    let a = dict.create(99); 
    let b = dict.create(42); 
    let mut set = ::std::collections::BTreeSet::new(); 
    set.insert(dict.ord_key(a)); 
    set.insert(dict.ord_key(b)); 
    println!("{:#?}", set); 
    let c = dict.create(1000); 
    let d = dict.create(0); 
    set.insert(dict.ord_key(c)); 
    set.insert(dict.ord_key(d)); 
    println!("{:#?}", set); 
} 
+0

@ker:私はマップごとの辞書を許可する2番目のものを作っておもちゃにしようとしていました...そして正直言って私は大成功を収めました! –

+0

@ker:私は 'BTreeMap'を再ラッピングすることを考えていましたが、メソッドがスレッドローカル変数を切り替える(そして呼び出しの終わりに前の値に戻す)のが呼び出されるたびに...しかし、スレッドローカル変数のための「AnyMap」は一般的な方法で表現することができます(たぶん汎用性を放棄する必要があります)。そして生涯は私に安全でないコードを使用させてしまいます。むしろ痛いです。 –

+0

@ker:コメントがクリーンアップされた場合に失われないように、私はあなたの例を答えに加えました。もう一つの解決策は、答えとして提供することです。その場合、私はそれを私のものから取り除き、あなたにアップコートします:) –

関連する問題