6

可変次元の配列のようなコードを書いていました。私がしていることは、線形の基礎となるコレクションを維持し、それをインデックス アクセス メソッドでラップすることです。データ構造の次元がわからないので、次のように書きます

def apply(i: Int*): Double = ...

そして、それは完璧に機能します。ただし、メソッドと += のような演算子を更新するために同じことを行うことはできないため、次のようなメソッドを記述することになります。

def set(v: Double, i: Int*) ...
def add(v: Double, i: Int*) ...

これは問題ありませんが、私が本当に欲しいものではありません。更新に関する問題は、次の 2 つの方法で修正される可能性があると思います。

  1. update 関数の引数の順序を変更すると、見た目がおかしくなります。
  2. 最後のものとしてではなく、可変長の引数を許可します。この質問は一般的な設定で尋ねられることがわかり、カリー化関数を使用して解決できますが、ここでは適用されません。

+= に関する問題はより複雑に見え、インデックスが固定長の場合にも存在します。+= 演算子を持つオブジェクトを追加し、 this(...) を使用してオブジェクトを取得することもできます (これにより、 this(...) += v は期待どおりに何らかのメソッドを呼び出します)。適用方法。

上記の質問のいずれかに対する解決策がある場合、またはこのようなコードを記述できない理由がある場合は、アイデアを共有してください! ありがとう〜

4

3 に答える 3

6

updateこれは主にシンタックス シュガーであり、特定のメソッド シグネチャに対応していないため、Scala ではかなり特殊なアーティファクトです。これは、私たちが創造的になり、アップデートにアリティ ポリモーフィック シグネチャを与えることができることを意味します。

scala> class Indexed { def update[P <: Product](p: P) = p }
defined class Indexed

scala> val i = new Indexed
i: Indexed = Indexed@1ea0e836

scala> i(0) = 1.0
res0: (Int, Double) = (0,1.0)

scala> i(0, 1) = 1.0
res1: (Int, Int, Double) = (0,1,1.0)

scala> i(0, 1, 2) = 1.0
res2: (Int, Int, Int, Double) = (0,1,2,1.0)

scala> i(0, 1, 2, 3) = 1.0
res3: (Int, Int, Int, Int, Double) = (0,1,2,3,1.0)

現状では、これにより、LHS のインデックスの型と RHS の値の型が完全に制約されないままになります。

scala> i(23, true, 'c') = "foo"
res4: (Int, Boolean, Char, String) = (23,true,c,foo)

しかし、のない2.0.0-SNAPSHOTのタプルの新しいサポートによって提供されるいくつかの暗黙の証拠を使用して、それを修正できます。

scala> import shapeless._
import shapeless._

scala> import syntax.tuple._
import syntax.tuple._

scala> class Indexed {
     |   def update[P <: Product, I](p: P)
     |     (implicit
     |       init:   TupleInit.Aux[P, I],
     |       toList: TupleToList[I, Int],
     |       last:   TupleLast.Aux[P, Double]) = (toList(init(p)), last(p))
     | }
defined class Indexed

scala> val i = new Indexed
i: Indexed = Indexed@76ab909a

scala> i(0) = 1.0
res10: (List[Int], Double) = (List(0),1.0)

scala> i(0, 1) = 2.0
res11: (List[Int], Double) = (List(0, 1),2.0)

scala> i(0, 1, 2) = 3.0
res12: (List[Int], Double) = (List(0, 1, 2),3.0)

scala> i(0, 1, 2, 3) = 4.0
res13: (List[Int], Double) = (List(0, 1, 2, 3),4.0)

scala> i(0, 1, 2) = "foo" // Doesn't compile
<console>:22: error: could not find implicit value for parameter
  last: shapeless.ops.tuple.TupleLast.Aux[(Int, Int, Int, String),Double]
              i(0, 1, 2) = "foo" // Doesn't compile
                         ^

scala> i(23, "foo", true) = 5.0 // Doesn't compile
<console>:22: error: could not find implicit value for parameter
  toList: shapeless.ops.tuple.TupleToList[I,Int]
              i(23, "foo", true) = 5.0 // Doesn't compile
                                 ^
于 2013-07-27T15:22:52.950 に答える
2

私が今見ている最も簡単な解決策はupdate、サポートしたいすべての次元に対して、さまざまなオーバーロードを用意することです。使用する最大次元が 10 であると判断できるとします。これは、10 個のオーバーロードが必要であることを意味します。これはあまり実用的ではないように思えるかもしれませんが、簡単に抽象化できるため、実際には非常に実用的です。

trait MultiKeyUpdate[K, V] {
  def doUpdate( k: K* )( v: V )
  def update(k1: K, v: V) { doUpdate( k1 )( v ) }
  def update(k1: K, k2: K, v: V) { doUpdate( k1, k2 )( v ) }
  def update(k1: K, k2: K, k3: K, v: V) { doUpdate( k1, k2, k3 )( v ) }  
  // ... and so on, up until max dimension ...
}

使用法:

class C extends MultiKeyUpdate[Int, Double] {
  def apply(i: Int*): Double = {
    println("Returning element " + i.mkString("[",",","]"))
    123
  }
  def doUpdate( i: Int* )( v: Double ) {
    println("Updating element " + i.mkString("[",",","]") + " to value " + v)
  }
}

そしてREPLでいくつかのテスト:

scala> val o = new C
o: C = C@12798c1
scala> o(1,2,3)
Returning element [1,2,3]
res3: Double = 123.0
scala> o(1,2,3) = 5.0
Updating element [1,2,3] to value 5.0
scala> o(1,2,3) += 7.0
Returning element [1,2,3]
Updating element [1,2,3] to value 130.0
于 2012-11-17T15:53:00.953 に答える
1
class SetMe {
  def set(i: Int*)(v: Double) { println("Set "+v+" with "+i.mkString(",")) }
}

scala> (new SetMe).set(4,7,19,3)(math.Pi)
Set 3.141592653589793 with 4,7,19,3

updateただし、そのトリックをそれ自体で行うことはできません。機能強化リクエストを提出する価値があるかもしれません。

于 2012-11-16T22:32:46.100 に答える