Простой способ с большим количеством вырезок и вставок, перегрузка метода для каждой артерии кортежей:
def range(r: (Int, Int), s: (Int, Int)) =
for { p1 <- r._1 to s._1
p2 <- r._2 to s._2 } yield (p1, p2)
def range(r: (Int, Int, Int), s: (Int, Int, Int)) =
for { p1 <- r._1 to s._1
p2 <- r._2 to s._2
p3 <- r._3 to s._3 } yield (p1, p2, p3)
def range(r: (Int, Int, Int, Int), s: (Int, Int, Int, Int)) =
for // etc up to 22
В качестве альтернативы:
def range(p1: Product, p2: Product) = {
def toList(t: Product): List[Int] =
t.productIterator.toList.map(_.asInstanceOf[Int])
def toProduct(lst: List[Int]) = lst.size match {
case 1 => Tuple1(lst(0))
case 2 => Tuple2(lst(0), lst(1))
case 3 => Tuple3(lst(0), lst(1), lst(2))
//etc up to 22
}
def go(xs: List[Int], ys: List[Int]): List[List[Int]] = {
if(xs.size == 1 || ys.size == 1) (xs.head to ys.head).toList.map(List(_))
else (xs.head to ys.head).toList.flatMap(i => go(xs.tail, ys.tail).map(i :: _))
}
go(toList(p1), toList(p2)) map toProduct
}
кажется, работает:
scala> range((1,2,4), (2,5,6))
res66: List[Product with Serializable] = List((1,2,4), (1,2,5), (1,2,6),
(1,3,4), (1,3,5), (1,3,6), (1,4,4), (1,4,5), (1,4,6), (1,5,4), (1,5,5),
(1,5,6), (2,2,4), (2,2,5), (2,2,6), (2,3,4), (2,3,5), (2,3,6), (2,4,4),
(2,4,5), (2,4,6), (2,5,4), (2,5,5), (2,5,6))
Ваша основная проблема заключается в том, что, поскольку Scala имеет статическую типизацию, метод должен иметь тип возвращаемого значения, поэтому у вас никогда не будет единственного метода, который возвращает и Seq[(Int, Int)]
, и Seq[(Int, Int, Int)]
, и все другие артерии кортежа. , Лучшее, что вы можете сделать, - это использовать ближайший тип, который охватывает все выходы, в данном случае Product with Serializable
. Вы можете, конечно, выполнить приведение к результату, например, res0.map(_.asInstanceOf[(Int, Int, Int)])
.
Перегрузка метода, как в первом примере, позволяет вам использовать другой тип возвращаемого значения для каждой арности, поэтому вам не нужно выполнять приведение.