如何在scala中编写元组范围函数?

发布于 2024-12-07 22:17:41 字数 774 浏览 0 评论 0原文

我想要以下功能 range((1,1), (2,2)) 其返回值

Seq[(Int,Int)]((1,1),(1,2),(2,1),(2,2))

1 到 2 的一维范围类似

该函数应该适用于任何 scala 元组 (即 Tuple2、Tuple3、Tuple4,...)并且类型安全。

我尝试过

    def tupleRange[T <: Product](t1:T, t2:T):Seq[T] = {
        assert(t1.productArity == t2.productArity)
        def tail(t:Product):Product = sys.error("todo"); 
        def join(i:Int, p:Product):T = sys.error("todo");
        for(
v <- t1.productElement(0).asInstanceOf[Int] to t2.productElement(0).asInstanceOf[Int]; 
v2 <- tupleRange(tail(t1), tail(t2)))
            yield join(v,v2)
    }
    implicit def range[T <:Product](p1:T) = new { def to(p2:T) = tupleRange(p1,p2)}

但我认为我选择了错误的方向。

I want following function
range((1,1), (2,2)) which return

Seq[(Int,Int)]((1,1),(1,2),(2,1),(2,2))

It is analog for one dimensional range with 1 to 2

The function should work for any scala tuple (i.e. Tuple2, Tuple3, Tuple4, ...) and be typesafe.

I've tried with

    def tupleRange[T <: Product](t1:T, t2:T):Seq[T] = {
        assert(t1.productArity == t2.productArity)
        def tail(t:Product):Product = sys.error("todo"); 
        def join(i:Int, p:Product):T = sys.error("todo");
        for(
v <- t1.productElement(0).asInstanceOf[Int] to t2.productElement(0).asInstanceOf[Int]; 
v2 <- tupleRange(tail(t1), tail(t2)))
            yield join(v,v2)
    }
    implicit def range[T <:Product](p1:T) = new { def to(p2:T) = tupleRange(p1,p2)}

But I think I've chosen wrong direction.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

望笑 2024-12-14 22:17:41

我建议和@ziggystar 上面建议的一样。使用 List[Int] 而不是 Int 的元组。

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> def range(xs: List[Int], ys: List[Int]): List[List[Int]] = {
     |   (xs, ys).zipped.map((x, y) => List.range(x, y + 1)).sequence
     | }
range: (xs: List[Int], ys: List[Int])List[List[Int]]

scala> range(List(1, 2, 4), List(2, 5, 6))
res29: List[List[Int]] = List(List(1, 2, 4), List(1, 2, 5), List(1, 2, 6), 
List(1, 3, 4), List(1, 3, 5), List(1, 3, 6), List(1, 4, 4), List(1, 4, 5), 
List(1, 4, 6), List(1, 5, 4), List(1, 5, 5), List(1, 5, 6), List(2, 2, 4), 
List(2, 2, 5), List(2, 2, 6), List(2, 3, 4), List(2, 3, 5), List(2, 3, 6), 
List(2, 4, 4), List(2, 4, 5), List(2, 4, 6), List(2, 5, 4), List(2, 5, 5), 
List(2, 5, 6))

此实现假设 xsys 是有序的并且具有相同的长度。

I'd suggest the same thing that @ziggystar suggested above. Use List[Int] instead of tuples of Ints.

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> def range(xs: List[Int], ys: List[Int]): List[List[Int]] = {
     |   (xs, ys).zipped.map((x, y) => List.range(x, y + 1)).sequence
     | }
range: (xs: List[Int], ys: List[Int])List[List[Int]]

scala> range(List(1, 2, 4), List(2, 5, 6))
res29: List[List[Int]] = List(List(1, 2, 4), List(1, 2, 5), List(1, 2, 6), 
List(1, 3, 4), List(1, 3, 5), List(1, 3, 6), List(1, 4, 4), List(1, 4, 5), 
List(1, 4, 6), List(1, 5, 4), List(1, 5, 5), List(1, 5, 6), List(2, 2, 4), 
List(2, 2, 5), List(2, 2, 6), List(2, 3, 4), List(2, 3, 5), List(2, 3, 6), 
List(2, 4, 4), List(2, 4, 5), List(2, 4, 6), List(2, 5, 4), List(2, 5, 5), 
List(2, 5, 6))

This implementation assumes that xs and ys are ordered and have same length.

染火枫林 2024-12-14 22:17:41

首先,考虑一下:

scala> 1 to 10
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

为元组提供类似的东西会很好,对吧?

class RangedTuple(t: Tuple2[Int, Int]) {
  def to(t2: Tuple2[Int, Int]) = {
    (t, t2) match {
      case ((a1: Int, a2: Int), (b1: Int, b2: Int)) => {
        (for {
          i <- a1 to b1
        } yield (a1 to b1).map(j => (i, j))).flatMap(k => k)
      }
    }
  }
}

implicit def t2rt(t: Tuple2[Int, Int]) = new RangedTuple(t)

这将为您提供以下信息:

scala> (1, 1) to (2, 2)
res1: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,1), (1,2), (2,1), (2,2))

scala> (1, 1) to (3, 3)
res2: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3))

这对您有用吗?

First, consider this:

scala> 1 to 10
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

It'd be nice to have something like that for Tuples, right?

class RangedTuple(t: Tuple2[Int, Int]) {
  def to(t2: Tuple2[Int, Int]) = {
    (t, t2) match {
      case ((a1: Int, a2: Int), (b1: Int, b2: Int)) => {
        (for {
          i <- a1 to b1
        } yield (a1 to b1).map(j => (i, j))).flatMap(k => k)
      }
    }
  }
}

implicit def t2rt(t: Tuple2[Int, Int]) = new RangedTuple(t)

This gives you the following:

scala> (1, 1) to (2, 2)
res1: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,1), (1,2), (2,1), (2,2))

scala> (1, 1) to (3, 3)
res2: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3))

Does that work for you?

鸠书 2024-12-14 22:17:41

每个元组数量都需要不同的版本(但您可以使用预处理器来生成每个版本)。这是我的实现(这是懒惰的):

def range2( range: Range ): Seq[(Int,Int)] = range.toStream.map( i => (i,i) )

您可以将其用作:

scala> range2( 1 to 10 )
res3: Seq[(Int, Int)] = Stream((1,1), ?)

You'll need a different version for each tuple-arity (but you can use a preprocessor to generate each version). Here is my implementation (which is lazy):

def range2( range: Range ): Seq[(Int,Int)] = range.toStream.map( i => (i,i) )

You can use it as:

scala> range2( 1 to 10 )
res3: Seq[(Int, Int)] = Stream((1,1), ?)
记忆消瘦 2024-12-14 22:17:41

使用大量剪切和粘贴的简单方法,为每个元组重载方法:

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 Serialized。您当然可以对结果进行转换,例如 res0.map(_.asInstanceOf[(Int, Int, Int)])

如第一个示例中那样重载该方法允许您为每个数量使用不同的返回类型,因此您不需要进行任何转换。

Simple way with lots of cut and paste, overload the method for each tuple arity:

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

Alternatively:

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
} 

seems to work:

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))

Your basic problem is that since Scala is statically typed, the method needs to have a return type, so you can never have a single method that returns both a Seq[(Int, Int)] and a Seq[(Int, Int, Int)] and all the other arities of tuple. The best you can do is to use the closest type that covers all of the outputs, in this case Product with Serializable. You can of course do a cast on the result e.g. res0.map(_.asInstanceOf[(Int, Int, Int)]).

Overloading the method as in the first example allows you a different return type for each arity, so you don't need to do any casting.

萌︼了一个春 2024-12-14 22:17:41

一个迭代器,并使用两个 Seq 而不是两个元组进行初始化怎么样?

这是一个 Cartesian 类,它扩展了 Iterator

def rangeIterator (froms: Seq[Int], tos: Seq[Int]) = {

  def range (froms: Seq[Int], tos: Seq[Int]) : Seq[Seq[Int]] = 
    if (froms.isEmpty) Nil else 
    Seq (froms.head to tos.head) ++ range (froms.tail, tos.tail) 

  new Cartesian (range (froms, tos))
}  

用法:

scala> val test = rangeIterator (Seq(1, 1), Seq(2, 2))
test: Cartesian = non-empty iterator    
scala> test.toList 
res38: List[Seq[_]] = List(List(1, 1), List(2, 1), List(1, 2), List(2, 2))

scala> val test = rangeIterator (Seq(1, 0, 9), Seq(2, 2, 11))
test: Cartesian = non-empty iterator
scala> test.toList 
res43: List[Seq[_]] = List(List(1, 0, 9), List(2, 0, 9), List(1, 1, 9), List(2, 1, 9), List(1, 2, 9), List(2, 2, 9), List(1, 0, 10), List(2, 0, 10), List(1, 1, 10), List(2, 1, 10), List(1, 2, 10), List(2, 2, 10), List(1, 0, 11), List(2, 0, 11), List(1, 1, 11), List(2, 1, 11), List(1, 2, 11), List(2, 2, 11))

How about an Iterator, and using two Seq instead of two tuples for initialization?

Here is a class Cartesian, which extends Iterator.

def rangeIterator (froms: Seq[Int], tos: Seq[Int]) = {

  def range (froms: Seq[Int], tos: Seq[Int]) : Seq[Seq[Int]] = 
    if (froms.isEmpty) Nil else 
    Seq (froms.head to tos.head) ++ range (froms.tail, tos.tail) 

  new Cartesian (range (froms, tos))
}  

usage:

scala> val test = rangeIterator (Seq(1, 1), Seq(2, 2))
test: Cartesian = non-empty iterator    
scala> test.toList 
res38: List[Seq[_]] = List(List(1, 1), List(2, 1), List(1, 2), List(2, 2))

scala> val test = rangeIterator (Seq(1, 0, 9), Seq(2, 2, 11))
test: Cartesian = non-empty iterator
scala> test.toList 
res43: List[Seq[_]] = List(List(1, 0, 9), List(2, 0, 9), List(1, 1, 9), List(2, 1, 9), List(1, 2, 9), List(2, 2, 9), List(1, 0, 10), List(2, 0, 10), List(1, 1, 10), List(2, 1, 10), List(1, 2, 10), List(2, 2, 10), List(1, 0, 11), List(2, 0, 11), List(1, 1, 11), List(2, 1, 11), List(1, 2, 11), List(2, 2, 11))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文