Подтвердить что ты не робот

Есть ли способ иметь кортежи с именованными полями в Scala, подобно анонимным классам на С#?

Смотрите: Могу ли я указать значимое имя для анонимного класса на С#?

В С# вы можете написать:

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

Но в Scala я в итоге пишу:

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

Scala поддерживает безопасность типов за счет использования generics (как и С#), но теряет читаемость имени каждого поля, например, вместо "ID" я использую "_1".

Есть ли что-нибудь подобное в Scala?

4b9b3361

Ответ 1

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

Хорошо, дайте понять. Это использует отражение на Scala 2.7 и Scala 2.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 также отражается, а также кэширует, но использует более эффективную технику кэширования, которая должна обеспечивать лучшую общую производительность. Для справки: очистка Scala 2.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";
      ()
    }
  }
}

Ответ 2

Вы также можете назвать части кортежа, который вы назначаете, например:

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

Вы также можете использовать это как:

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

Когда я впервые прочитал о синтаксисе _x, я подумал, что это здорово, и много использовал его. Я с тех пор в основном перестала его использовать, так как, когда мне приходится смотреть на код, который я написал два месяца назад, я должен потратить массу времени на то, чтобы выяснить, что такое типы _1, _2 и т.д. Я полагаю, что ретроспективно, что id гораздо читабельнее, чем pair._1.

Это также можно использовать внутри таких функций, как map, filter и т.д., например:

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

Обратите внимание, что в filter вы можете использовать _ для элементов, которые вы не собираетесь использовать в теле функции. Это может быть полезно при просмотре кода таким образом, чтобы определить, какие части структур используются и как создаются значения.

Ответ 3

Я бы просто сделал класс case:

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

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

Не уверен, насколько он эффективен, как Даниэль ответьте, но я ожидаю, что он будет таким же (я буду благодарен за комментарии). В любом случае, я считаю его более читаемым, используя только одну дополнительную линию, которая является общей, если у вас более одного экземпляра, если E. Также вы можете добавить метод к классу case, например:

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

val e = E(5,"Prashant")
assert(e hasId 5)

Ответ 4

Как говорит Juh_, расширение класса case должно делать то, что вы хотите:

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

Классы классов предоставляют метод equals, а также расширяют признак Product, который является тем же самым признаком, что и кортежи Scala. Возможно, в будущем они также будут расширять черты ProductN.

Если вы используете анонимные классы, как предложено в некоторых других ответах, вы не получаете настоящие кортежи! Например, вы не получаете метод equals:

scala> val x = new { val count = 5 }
x: AnyRef{val count: Int} = [email protected]

scala> val y = new { val count = 5 }
y: AnyRef{val count: Int} = [email protected]

scala> x == y
res4: Boolean = false

По состоянию на Scala 2.11 расширение Tuple2 действительно работает, но это устарело, потому что вы не должны расширять классы case.

Вы также можете расширить признак Product2, но это не обеспечивает реализацию каких-либо методов, поэтому вам придется писать все методы самостоятельно.

Возможно, вы также можете использовать Shapeless HList, что даст вам множество причудливых функций за счет добавления внешней зависимости.

Я также попробовал библиотеку jaqen, но это не скомпилировалось для меня в Scala 2.11.

В настоящее время я использую Scala 2.11, поэтому я не могу гарантировать, что этот совет относится к другим версиям Scala.

Ответ 5

Scala 2.8 улучшило систему типов до такой степени, что возможно иметь статически и неоднородно типизированные массивы и списки, поэтому, возможно, то же самое можно было бы сделать с картами. Проверьте блог Jesper Nordenberg в разделе "Списки типов и разнородные типизированные массивы" для его реализации.