Scala “生成懒惰”;螺旋形;在斯卡拉

Scala “生成懒惰”;螺旋形;在斯卡拉,scala,yield,spiral,Scala,Yield,Spiral,任务: 对于二维数组中的给定位置,生成位于半径中的周围位置列表 例如: input: (1, 1) radius: 1 output: ( (0, 0), (1, 0), (2, 0), (0, 1), (2, 1), (0, 2), (1, 2), (2, 2) ). 我写了这样的东西 def getPositions(x:Int, y:Int, r:Int) = { for(radius <- 1 to r) yield

任务: 对于二维数组中的给定位置,生成位于半径中的周围位置列表

例如:

input: (1, 1)
radius: 1
output: ( (0, 0), (1, 0), (2, 0), 
          (0, 1),         (2, 1),
          (0, 2), (1, 2), (2, 2) ).
我写了这样的东西

def getPositions(x:Int, y:Int, r:Int) = {
  for(radius <- 1 to r) yield {
    List(
      for (dx <- -radius to radius) yield Pair(x + dx, y - radius),
      for (dx <- -radius to radius) yield Pair(x + dx, y + radius),
      for (dy <- -radius to radius) yield Pair(x + radius, y + dy),
      for (dy <- -radius to radius) yield Pair(x - radius, y + dy)
    )
  }
}
def getPositions(x:Int,y:Int,r:Int)={

对于(radius,您需要展平列表(两次),这样做:

def getPositions(x:Int, y:Int, r:Int) = {
  for(radius <- 1 to r) yield {
    List(
      for (dx <- -radius to radius) yield Pair(x + dx, y - radius),
      for (dx <- -radius to radius) yield Pair(x + dx, y + radius),
      for (dy <- -radius to radius) yield Pair(x + radius, y + dy),
      for (dy <- -radius to radius) yield Pair(x - radius, y + dy)
    ).flatten
  }
}.flatten
我使用了
def p
方法来显示真正的惰性。每次创建
对时,它都会被调用。在惰性解决方案中,您只需要按需执行此操作。

尝试以下操作:

object Spiral
{
    def
    getPositions(x: Int, y: Int, r: Int): Seq[(Int, Int)] = {
      for { radius <- 1 to r
            dx <- -radius to radius
            dy <- -radius to radius
            if dx != 0 || dy != 0
      } yield
          (x + dx, y + dy)
    }


    def
    main(args: Array[String]): Unit = {
        printf("getPositions(1, 1, 1): %s%n", getPositions(0, 0, 1).mkString("{ ", ", ", " }"))
    }
}

您可以直接形成您的范围,并使用
flatMap
++
在创建列表时将它们连接在一起,并且您可能还希望沿着圆形方向:

def getPositions(x: Int, y: Int, r: Int) = {
  (1 to r) flatMap (radius => {
    val dx = -radius to radius
    val dy = -(radius-1) to (radius-1)
    dx.map(i => (x+i, y+radius)) ++ dy.map(i => (x+radius, y-i)) ++
    dx.map(i => (x-i, y-radius)) ++ dy.map(i => (x-radius, y+i))
  })
}
如果您真的希望结果是惰性的,那么必须对惰性组件执行相同的操作:

def getPositions(x: Int, y: Int, r: Int) = {
  Stream.range(1,r+1) flatMap (radius => {
    val dx = Stream.range(-radius,radius+1)
    val dy = Stream.range(-(radius+1),radius)
    dx.map(i => (x+i, y+radius)) ++ dy.map(i => (x+radius, y-i)) ++
    dx.map(i => (x-i, y-radius)) ++ dy.map(i => (x-radius, y+i))
  })
}

编辑:修复了dx与dy打字错误。

这里有一些解决此问题的方法。首先,如果您不关心订单,只关心位置,则可以:

def getPositions(x:Int, y:Int, r:Int) = for {
  yr <- y - r to y + r
  xr <- x - r to x + r
  if xr != x || yr != y
} yield (xr, yr)
这将返回一个
迭代器
,您可以使用
next
对其进行迭代。使用
hasNext
检查结尾

您可以用
List
Stream
或诸如此类的东西替换
Iterator
,得到一个完全生成的集合

现在,假设你想要一个从中心开始,一次移动一个位置的螺旋。我们可以这样做:

def getPositions(x:Int, y:Int, r:Int) = new Iterator[(Int, Int)] {
  private var currentX = x
  private var currentY = y
  private var currentR = 1
  private var incX = 0
  private var incY = 1
  def next = {
    currentX += incX
    currentY += incY
    val UpperLeft = (x - currentR, y + currentR)
    val UpperRight = (x + currentR, y + currentR)
    val LowerLeft = (x - currentR, y - currentR)
    val LowerRight = (x + currentR, y - currentR)
    val PrevSpiral = (x, y + currentR)
    val NextSpiral = (x - 1, y + currentR)
    (currentX, currentY) match {
      case NextSpiral => incX = 1; incY = 1; currentR += 1
      case PrevSpiral => incX = 1; incY = 0
      case UpperLeft => incX = 1; incY = 0
      case UpperRight => incX = 0; incY = -1
      case LowerRight => incX = -1; incY = 0
      case LowerLeft => incX = 0; incY = 1
      case _ =>
    }
    if (currentR > r)
      throw new NoSuchElementException("next on empty iterator")
    (currentX, currentY)
  }
  def hasNext = currentR <= r
}
def getPositions(x:Int,y:Int,r:Int)=新迭代器[(Int,Int)]{
专用var currentX=x
专用var电流y=y
专用var currentR=1
私有变量incX=0
私有变量incY=1
def next={
电流x+=incX
电流y+=incY
val左上=(x-currentR,y+currentR)
val UpperRight=(x+currentR,y+currentR)
val LowerLeft=(x-currentR,y-currentR)
val LowerRight=(x+电流R,y-电流R)
val Prev螺旋=(x,y+电流R)
val NextSpiral=(x-1,y+currentR)
(当前x,当前y)匹配{
案例NextSpiral=>incX=1;incY=1;currentR+=1
大小写PrevSpiral=>incX=1;incY=0
大小写左上=>incX=1;incY=0
大写右=>incX=0;incY=-1
case LowerRight=>incX=-1;incY=0
大小写下限=>incX=0;incY=1
案例=>
}
如果(当前r>r)
抛出新的NoSuchElementException(“空迭代器上的下一步”)
(电流X,电流Y)
}

def hasNext=currentR这是一条沿着边缘流动的流

假设输入(3,3),2给出

然后,您可以使用以下选项:

def border(p: (Int,Int), r: Int) = {
  val X1 = p._1 - r
  val X2 = p._1 + r
  val Y1 = p._2 - r
  val Y2 = p._2 + r
  def stream(currentPoint: (Int,Int)): Stream[(Int,Int)] = {
    val nextPoint = currentPoint match {
      case (X1, Y1) => (X1+1, Y1)
      case (X2, Y2) => (X2-1, Y2)
      case (X1, Y2) => (X1, Y2-1)
      case (X2, Y1) => (X2, Y1+1)
      case (x, Y1) => (x+1, Y1)
      case (x, Y2) => (x-1, Y2)
      case (X1, y) => (X1, y-1)
      case (X2, y) => (X2, y+1)
    }
    Stream.cons(nextPoint, if (nextPoint == (X1,Y1)) Stream.empty else stream(nextPoint))
  }
  stream((X1,Y1))
}
用法:

scala> val b = border((3,3),2)
b: Stream[(Int, Int)] = Stream((2,1), ?)

scala> b.toList
res24: List[(Int, Int)] = List((2,1), (3,1), (4,1), (5,1), (5,2), (5,3), (5,4), (5,5), (4,5), (3,5), (2,5), (1,5), (1,4), (1,3), (1,2), (1,1))

生成的列表是否仍然是“懒惰的”(就像python中的生成器)?不,不要被
yield
关键字所迷惑。它不是生成器。我认为,逻辑不正确,无法获得所需的螺旋。是的,逻辑有几个问题(顺序、重复角点等),但你明白了。你重复所有的角点。我怀疑你是否想这样做。将第二个for循环的值从
-(radius-1)
改为
(radius-1)
。这里有人帮忙吗?如果有,请选择一个正确的答案。另外,机器人进展如何?Ups,一个错误。这将输出半径中的所有点,除了圆心。这不是我想要的。我只需要边界,它不会像写的那样工作,因为每次都会生成一个缺少圆心的正方形。你需要吗er需要删除
radius@xap4o:看看雷克斯说的。我复制了你的radius-1螺旋,但没有尝试更大的半径。
def getPositions(x:Int, y:Int, r:Int) = new Iterator[(Int, Int)] {
  private var currentX = x
  private var currentY = y
  private var currentR = 1
  private var incX = 0
  private var incY = 1
  def next = {
    currentX += incX
    currentY += incY
    val UpperLeft = (x - currentR, y + currentR)
    val UpperRight = (x + currentR, y + currentR)
    val LowerLeft = (x - currentR, y - currentR)
    val LowerRight = (x + currentR, y - currentR)
    val PrevSpiral = (x, y + currentR)
    val NextSpiral = (x - 1, y + currentR)
    (currentX, currentY) match {
      case NextSpiral => incX = 1; incY = 1; currentR += 1
      case PrevSpiral => incX = 1; incY = 0
      case UpperLeft => incX = 1; incY = 0
      case UpperRight => incX = 0; incY = -1
      case LowerRight => incX = -1; incY = 0
      case LowerLeft => incX = 0; incY = 1
      case _ =>
    }
    if (currentR > r)
      throw new NoSuchElementException("next on empty iterator")
    (currentX, currentY)
  }
  def hasNext = currentR <= r
}
{(1,1), (2,1), (3,1), (4,1), (5,1),
 (1,2),                      (5,2),
 (1,3),                      (5,3),
 (1,4),                      (5,4),
 (1,5), (2,5), (3,5), (4,5), (5,5)}
def border(p: (Int,Int), r: Int) = {
  val X1 = p._1 - r
  val X2 = p._1 + r
  val Y1 = p._2 - r
  val Y2 = p._2 + r
  def stream(currentPoint: (Int,Int)): Stream[(Int,Int)] = {
    val nextPoint = currentPoint match {
      case (X1, Y1) => (X1+1, Y1)
      case (X2, Y2) => (X2-1, Y2)
      case (X1, Y2) => (X1, Y2-1)
      case (X2, Y1) => (X2, Y1+1)
      case (x, Y1) => (x+1, Y1)
      case (x, Y2) => (x-1, Y2)
      case (X1, y) => (X1, y-1)
      case (X2, y) => (X2, y+1)
    }
    Stream.cons(nextPoint, if (nextPoint == (X1,Y1)) Stream.empty else stream(nextPoint))
  }
  stream((X1,Y1))
}
scala> val b = border((3,3),2)
b: Stream[(Int, Int)] = Stream((2,1), ?)

scala> b.toList
res24: List[(Int, Int)] = List((2,1), (3,1), (4,1), (5,1), (5,2), (5,3), (5,4), (5,5), (4,5), (3,5), (2,5), (1,5), (1,4), (1,3), (1,2), (1,1))