1

辞書の値を更新するカスタム オペレーターを作成するエレガントな方法はありますか?

より具体的には、特定のキーに対応する整数値をインクリメントするプレフィックス演算子が必要です。

prefix operator +> {}

prefix func +> //Signature 
{
    ...
}

var d = ["first" : 10 , "second" : 33]
+>d["second"] // should update d to ["first" : 10 , "second" : 34]

これは、機能的な方法を使用して実現可能です。たとえば、配列内の要素の頻度を計算するには、次のようにします。

func update<K,V>(var dictionary: [K:V], key: K, value: V) -> [K:V] {
    dictionary[key] = value
    return dictionary
}

func increment<T>(dictionary: [T:Int], key: T) -> [T:Int] {
    return update(dictionary, key: key, value: dictionary[key].map{$0 + 1} ?? 1)
}

func histogram<T>( s: [T]) -> [T:Int] {
    return s.reduce([T:Int](), combine: increment)
}

let foo = histogram([1,4,3,1,4,1,1,2,3]) // [2: 1, 3: 2, 1: 4, 4: 2]

しかし、カスタム演算子を使用して同じことをしようとしています

4

3 に答える 3

1
var d = ["first" : 10 , "second" : 33]

d["second"]?++

演算子は次のように実装できます。

prefix operator +> {}
prefix func +> <I : ForwardIndexType>(inout i: I?) {
  i?._successorInPlace()
}

var dict = ["a":1, "b":2]

+>dict["b"]

dict // ["b": 3, "a": 1]

頻度関数がどのように得られるかはわかりませんが、つまり、辞書を構築している場合、最初からキーがないため、インクリメントするものは何もありません。ただし、それを行うためのクールな方法がたくさんあります。postfix を使用すると、次の++ことができます。

extension SequenceType where Generator.Element : Hashable {
  func frequencies() -> [Generator.Element:Int] {
    var result: [Generator.Element:Int] = [:]
    for element in self {
      result[element]?++ ?? {result.updateValue(1, forKey: element)}()
    }
    return result
  }
}

Airspeed Velocity は、別のクールな方法でツイートしました

extension Dictionary {
  subscript(key: Key, or or: Value) -> Value {
    get { return self[key] ?? or }
    set { self[key] = newValue }
  }
}

extension SequenceType where Generator.Element : Hashable {
  func frequencies() -> [Generator.Element:Int] {
    var result: [Generator.Element:Int] = [:]
    for element in self { ++result[element, or: 0] }
    return result
  }
}

または、文書化されていない関数を使用します。

extension SequenceType where Generator.Element : Hashable {
  func frequencies() -> [Generator.Element:Int] {
    var result: [Generator.Element:Int] = [:]
    for el in self {result[el]?._successorInPlace() ?? {result[el] = 1}()}
    return result
  }
}
于 2015-07-30T18:47:59.927 に答える
1

まず、関数 (カスタム オペレーターではなく) を使用してそれを行う方法を探します。(辞書から) 項目への参照を取得し、その値を更新する関数が必要です...inoutパラメータ型を呼び出す関数。

func increment(inout n: Int) {
    n++
}

var d = ["first" : 10 , "second" : 33]
increment(&d["first"]!)
print(d) // -> "[first: 11, second: 33]"

ディクショナリにある値を気にする必要はありませんinout。あらゆる種類の参照を取得して、直接更新します。(これは計算されたプロパティにも当てはまります。1 つinoutを渡すことができ、値を読み書きするときにセッターとゲッターを正しく通過します。)また、辞書を気にする必要がないため、実際には必要ありません。一般的であること — Intsを含む辞書で機能する関数が必要な場合は、 s で機能する関数を作成し、残りはInt任せてください。inout

ここで、カスタム オペレーターは単なる関数なので、関数のオペレーターを作成します。

prefix operator +> {}
prefix func +>(inout n: Int) {
    n++
}

ただし、それを呼び出すために求めていた構文を正確に使用することはできません。辞書検索は常に Optional 型になるため、ラップを解除する必要があります。

+>d["second"]  // error
+>d["second"]! // but this works — operators automatically make params inout as needed
print(d) // -> "[first: 11, second: 34]"
于 2015-07-30T19:04:03.070 に答える
0

これはおそらくあなたが探しているよりも少し醜いですが、一般的なオーバーロードされた演算子で安全でないミュータブル ポインターを使用して実現できます。

prefix operator +> {}

prefix func +><T>( value:UnsafeMutablePointer<T?> )
{
    print( value.memory )
    if let intValue = value.memory as? Int {
        value.memory = (intValue + 1) as? T
    }
}

var d = ["first" : 10 , "second" : 33]
print( d["second"] ) // Optional(33)
+>(&d["second"])
print( d["second"] ) // Optional(34)
于 2015-07-30T18:54:37.627 に答える