22

参照:C#で匿名クラスに意味のある名前を指定できますか?

C#では、次のように記述できます。

var e = new { ID = 5, Name= "Prashant" };
assertEquals( 5, e.ID )

しかし、Scalaでは私は次のように書くことになります。

var e = (5, "Prashant")
assertEquals( 5, e._1 )

Scalaはジェネリックス(C#と同様)を使用して型の安全性を維持しますが、各フィールドの名前の可読性を失います。たとえば、「ID」の代わりに「_1」を使用します。

Scalaにこのようなものはありますか?

4

5 に答える 5

23
object T {
  def main(args: Array[String]) {  
    val e = new { var id = 5; var name = "Prashant" }
    assert(e.id == 5)
  }
}

わかりました。明確にしましょう。これは、Scala2.7とScala2.8でリフレクションを使用します。これは、タイプがeこの場合、構造タイプであり、Scalaがリフレクションを介して処理するためです。クリーンアップ時に生成されたコードは次のとおりです(scalac -Xprint:cleanup):

package <empty> {
  final class T extends java.lang.Object with ScalaObject {
    private <synthetic> <static> var reflMethod$Cache1: java.lang.reflect.Method = null;
    private <synthetic> <static> var reflClass$Cache1: java.lang.Class = null;
    <synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = {
      if (T.this.reflMethod$Cache1.eq(null).||(T.this.reflClass$Cache1.ne(x$1)))
        {
          T.this.reflMethod$Cache1 = x$1.getMethod("id", Array[java.lang.Class]{});
          T.this.reflClass$Cache1 = x$1;
          ()
        };
      T.this.reflMethod$Cache1
    };
    @remote def $tag(): Int = scala.ScalaObject$class.$tag(T.this);
    def main(args: Array[java.lang.String]): Unit = {
      val e: java.lang.Object = {
        new T$$anon$1()
      };
      scala.this.Predef.assert(scala.Int.unbox({
        var exceptionResult1: java.lang.Object = _;
        try {
          exceptionResult1 = T.reflMethod$Method1(e.getClass()).invoke(e, Array[java.lang.Object]{})
        } catch {
          case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => {
            exceptionResult1 = throw $1$.getCause()
          }
        };
        exceptionResult1
      }.$asInstanceOf[java.lang.Integer]()).==(5))
    };
    def this(): object T = {
      T.super.this();
      ()
    }
  };
  final class T$$anon$1 extends java.lang.Object {
    private[this] var id: Int = _;
    <accessor> def id(): Int = T$$anon$1.this.id;
    <accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1;
    private[this] var name: java.lang.String = _;
    <accessor> def name(): java.lang.String = T$$anon$1.this.name;
    <accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1;
    def this(): T$$anon$1 = {
      T$$anon$1.this.id = 5;
      T$$anon$1.this.name = "Prashant";
      T$$anon$1.super.this();
      ()
    }
  }
}

いくつかのキャッシュが進行中ですが、私が交互に行った場合、idすでにnameキャッシュが無効になります。Scala 2.8はリフレクションもキャッシュも行いますが、より効率的なキャッシュ手法を使用しているため、全体的なパフォーマンスが向上します。参考までに、Scala2.8のクリーンアップは次のとおりです。

package <empty> {
  final class T extends java.lang.Object with ScalaObject {
    final private <synthetic> <static> var reflParams$Cache1: Array[java.lang.Class] = Array[java.lang.Class]{};
    @volatile
    private <synthetic> <static> var reflPoly$Cache1: scala.runtime.MethodCache = new scala.runtime.EmptyMethodCache();
    <synthetic> <static> def reflMethod$Method1(x$1: java.lang.Class): java.lang.reflect.Method = {
      var method1: java.lang.reflect.Method = T.reflPoly$Cache1.find(x$1);
      if (method1.ne(null))
        return method1
      else
        {
          method1 = x$1.getMethod("id", T.reflParams$Cache1);
          T.reflPoly$Cache1 = T.reflPoly$Cache1.add(x$1, method1);
          return method1
        }
    };
    def main(args: Array[java.lang.String]): Unit = {
      val e: java.lang.Object = {
        new T$$anon$1()
      };
      scala.this.Predef.assert(scala.Int.unbox({
        val qual1: java.lang.Object = e;
        {
          var exceptionResult1: java.lang.Object = _;
          try {
            exceptionResult1 = T.reflMethod$Method1(qual1.getClass()).invoke(qual1, Array[java.lang.Object]{})
          } catch {
            case ($1$ @ (_: java.lang.reflect.InvocationTargetException)) => {
              exceptionResult1 = throw $1$.getCause()
            }
          };
          exceptionResult1
        }.$asInstanceOf[java.lang.Integer]()
      }).==(5))
    };
    def this(): object T = {
      T.reflParams$Cache1 = Array[java.lang.Class]{};
      T.reflPoly$Cache1 = new scala.runtime.EmptyMethodCache();
      T.super.this();
      ()
    }
  };
  final class T$$anon$1 extends java.lang.Object {
    private[this] var id: Int = _;
    <accessor> def id(): Int = T$$anon$1.this.id;
    <accessor> def id_=(x$1: Int): Unit = T$$anon$1.this.id = x$1;
    private[this] var name: java.lang.String = _;
    <accessor> def name(): java.lang.String = T$$anon$1.this.name;
    <accessor> def name_=(x$1: java.lang.String): Unit = T$$anon$1.this.name = x$1;
    def this(): T$$anon$1 = {
      T$$anon$1.super.this();
      T$$anon$1.this.id = 5;
      T$$anon$1.this.name = "Prashant";
      ()
    }
  }
}
于 2009-12-01T17:54:47.500 に答える
18

次のように、割り当てるタプルの部分に名前を付けることもできます。

val (ID, Name) = (5, "Prashant")
assertEquals( 5, ID )

次のように使用することもできます。

val (ID, Name, Age) = functionThatReturnsATuple3
println("ID: " + ID + ", age: " + Age)

構文について最初に読んだとき、_xそれは素晴らしいと思い、よく使用しました。_1それ以来、2か月前に書いたコードを見る必要があるので、基本的には使用をやめました。たとえば、の種類などを理解するために多くの時間を費やす必要があり_2ます。id後から考えると、よりもはるかに読みやすいことは明らかだと思いますpair._1

mapこれは、次のような関数内でも使用できますfilter

val list: List[ (Int, String, Double) ] = ...
list map { case (id, name, time) => ... }
list filter { case (_, name, _) => name == "X" }

で、関数の本体で使用しない要素にsをfilter使用できることに注意してください。_これは、そのようなコードをざっと見て、構造のどの部分が使用されているか、および値がどのように構築されているかを確認するときに役立ちます。

于 2009-12-03T12:14:20.053 に答える
9

ケースクラスを作成するだけです:

object Yo{
  case class E(id: Int, name: String)

  def main(){
    val e = E(5,"Prashant")
    println("name="+e.name+", id="+e.id)
  }
}

ダニエルの答えと同じくらい効率的かどうかはわかりませんが、同じだと思います(コメントをいただければ幸いです)。いずれにせよ、 if のインスタンスが複数ある場合に共有される追加の行を 1 つだけ使用すると、より読みやすくなりますE。また、次のようにケース クラスにメソッドを追加することもできます。

case class E(id: Int, name: String){
  def hasId(id: Int) = this.id==id
}

val e = E(5,"Prashant")
assert(e hasId 5)
于 2015-09-21T10:19:30.987 に答える
4

Juh_ が示唆するように、ケース クラスを拡張すると、必要なことが行われます。

scala> case class E(id: Int, name: String)
defined class E

scala> val e = new E(5, "Prashant")
e: E = E(5,Prashant)

scala> e.name
res3: String = Prashant

Case クラスは equals メソッドを提供し、またProductトレイトを拡張します。これは、Scala タプルが拡張するのと同じトレイトです。将来的には、特性も拡張ProductNされる可能性があります。

他の回答で提案されているように匿名クラスを使用すると、実際のタプルにはなりません! たとえば、次の equals メソッドは取得できません。

scala> val x = new { val count = 5 }
x: AnyRef{val count: Int} = $anon$1@29ca901e

scala> val y = new { val count = 5 }
y: AnyRef{val count: Int} = $anon$1@1dfe2924

scala> x == y
res4: Boolean = false

Scala 2.11 の時点で拡張Tuple2は機能しますが、ケース クラスを拡張することは想定されていないため、これは非推奨です。

トレイトを拡張することもできますがProduct2、それはメソッドの実装を提供しないため、すべてのメソッドを自分で作成する必要があります。

おそらく、 ShapelessHListを使用することもできます。これにより、外部依存関係を追加することを犠牲にして、多くの優れた機能が提供されます。

Twitter のjaqenライブラリも試してみましたが、Scala 2.11 ではコンパイルできませんでした。

私は現在 Scala 2.11 を使用しているため、このアドバイスが他のバージョンの Scala に当てはまるかどうかは保証できません。

于 2016-08-30T15:29:51.867 に答える
1

Scala 2.8 では型システムが改善され、静的かつ異種の型付けされた配列とリストを使用できるようになったため、おそらくマップでも同じことができるようになりました。実装については、Jesper Nordenbergのブログ「Type Lists and Heterogeneously Typed Arrays」を参照してください。

于 2009-12-01T17:44:32.540 に答える