Как добавить или добавить элемент в кортеж в Scala

У меня есть кортеж, и я хочу добавить элемент, не теряя безопасности типов. Вот чего я хочу добиться:

val tuple = ("", 1, 1f) // (String, Int, Float)

val newTuple:(String, Int, Float, Double) = tuple :+ 1d

person EECOLOR    schedule 11.03.2013    source источник
comment
Я создал запрос функции для этого: issues.scala-lang.org/browse/SI -7305   -  person EECOLOR    schedule 27.03.2013


Ответы (4)


Стоит отметить, что для этого также можно написать генератор кода в несколько строк:

val tupadd = for (n <- 2 to 20) yield {
  val t = (0 until n).map(i => ('A'+i).toChar).mkString(", ")
  val u = ('A'+n).toChar
  val i = (0 until n).map(i => "x._"+(i+1)).mkString(", ")
  List(
    s"implicit class TupOps$n[$t](val x: ($t)) extends AnyVal {",
    s"  def :+[$u](y: $u) = ($i, y)",
    s"  def +:[$u](y: $u) = (y, $i)",
    "}"
  ).mkString("\n")
}

Распечатайте их, вставьте в файл, и все готово:

scala> implicit class TupOps2[A, B](val x: (A, B)) extends AnyVal {
     |   def :+[C](y: C) = (x._1, x._2, y)
     |   def +:[C](y: C) = (y, x._1, x._2)
     | }
defined class TupOps2

scala> (1,"salmon") :+ true
res15: (Int, String, Boolean) = (1,salmon,true)
person Rex Kerr    schedule 11.03.2013
comment
Я думаю, что это будет работать лучше, а также - person EECOLOR; 12.03.2013
comment
@EECOLOR - я ожидаю этого, но я не могу назвать это более элегантным (даже если это довольно просто). - person Rex Kerr; 12.03.2013
comment
Я подумал, что если это будет добавлено в библиотеку Scala по умолчанию, это устранит большинство решений от Class1 до Class22 в библиотеках. - person EECOLOR; 27.03.2013
comment
@EECOLOR - Ну, это не единственное, чего мне очень не хватает. Я также скучаю по одноэлементным сеттерам и одноэлементной карте. - person Rex Kerr; 27.03.2013
comment
Я изменил свой ответ, так как он не работал во всех случаях. В конце концов мне пришлось генерировать код. Я выбрал ваш ответ как правильный. - person EECOLOR; 19.04.2013

Shapeless теперь делает это. Добавление

import shapeless.syntax.std.tuple._

прежде чем ваш код просто скомпилируется.

person Alex Archambault    schedule 19.07.2014
comment
@ al3xaar al3xaar Как продолжить добавление к кортежу переменной val? Я получаю переназначение на ошибку val - person santhosh; 18.12.2015

Я нашел решение с помощью замечательной библиотеки Shapeless, и это HList

/*
  Define an implicit class to add the methods
  It accepts any tuple as we have typed it as Product
*/
implicit class TupleOps[A <: Product](t: A) {

  /*
    Declare the method to append

    B - The type of element we want to append
    L - The HList representing the tuple A
    P - The HList after appending B
    R - The final result
  */
  def :+[B, L <: HList, P <: HList, R <: Product](b: B)(
    /*
      We need some tools to help with the conversion

      hlister - Converts a tuple into an HList
      prepend - Can prepend one HList before another
      tupler  - Can convert an HList into a tuple
    */
    implicit hlister: HListerAux[A, L], prepend: PrependAux[L, B :: HNil, P], tupler: TuplerAux[P, R]):R =
      // Let the helpers do their job
      tupler(prepend(hlister(t), b :: HNil))

  /*
    The prepend method is similar to the append method but does not require
    the extra effort to append
  */
  def +:[B, L <: HList, R <: Product](b: B)(
    // Here we use the :: type of shapeless
    implicit hlister: HListerAux[A, L], tupler: TuplerAux[B :: L, R]):R =
      tupler(b :: hlister(t))
}

// usage is like this
("", 1, 1f) :+ 1d  //> res0: (String, Int, Float, Double) = ("",1,1.0,1.0)
1d +: ("", 1, 1f)  //> res1: (Double, String, Int, Float) = (1.0,"",1,1.0)

Редактировать

В некоторых случаях, когда вам нужно иметь дело с неявными преобразованиями, это решение не будет работать в сочетании с классами case. Теперь я вернулся к следующей реализации (на основе кода Рекса Керра)

def char(n: Int) = ('A' + n).toChar
def prop(n: Int) = "t._" + (n + 1)

val result =
  for (n <- 1 to 21) yield {

    val range = (0 until n)
    val tupleTypeParams = range map char mkString ", "
    val tupleProperties = range map prop mkString ", "

    val elementType = char(n)
    val elementProperty = prop(n)

    val first = n == 1
    val tupleType = if (first) s"Tuple1[$tupleTypeParams]" else s"($tupleTypeParams)"
    val tupleInstance = if (first) s"Tuple1($tupleProperties)" else s"($tupleProperties)"

    val resultType = s"($tupleTypeParams, $elementType)"

    s"""|implicit def tupleOps$n[$tupleTypeParams, $elementType] = 
        |  new TupleAppendOps[$tupleType, $elementType, $resultType] {
        |    def append(t: $tupleType, e: $elementType) = ($tupleProperties, e)
        |    def init(t: $resultType) = $tupleInstance
        |    def last(t: $resultType) = $elementProperty
        |  }""".stripMargin
  }

println(result mkString "\n")
person EECOLOR    schedule 11.03.2013

Вы можете попробовать мою экспериментальную библиотеку макросов, основанную на ветке Macro Paradise scala, по адресу https://github.com/leonardschneider/macrogen который делает именно ваш пример.

Однако сейчас у меня все еще есть некоторые ошибки, из-за которых я не могу связать вызовы.

person Leo    schedule 12.03.2013