题
任务: 用于2D阵列的给定位置产生位于半径周围的位置的列表。
例如:
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)
)
}
}
在此代码getPositions不返回点的sequance,但点的济源Tuple4的sequance。 我怎样才能“连击” 4所生成代码上市?或者是有我的任务更简洁的解决方案? (我很新的阶)。
P.S。 它实际上是对我的星际争霸机器人。
解决方案
您必须汇整清单(两次),因此这将做到:
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(i:Int, j:Int) = { print("eval"); Pair(i,j) }
def lazyPositions(x:Int, y:Int, r:Int) = {
(1 to r).toStream.flatMap{ radius =>
(-radius to radius).toStream.map(dx => P(x + dx, y - radius)) #:::
(-radius to radius).toStream.map(dx => P(x + dx, y + radius)) #:::
(-radius to radius).toStream.map(dy => P(x + radius, y + dy)) #:::
(-radius to radius).toStream.map(dy => P(x - radius, y + dy))
}
}
print(lazyPositions(1,1,1).take(3).toList) # prints exactly three times ‘eval’.
我使用def P
方法展现真实懒惰。每次,你会创建一个Pair
,它被调用。在一个懒惰的解决方案,你只想要这个需求。
其他提示
尝试这种情况:
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("{ ", ", ", " }"))
}
}
输出:
getPositions(1, 1, 1): { (-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1) }
您可以直接形成你的范围,并使用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)
这会给你指定的完全相同的输出。不过,想要一个Python式发电机,所以这将是比较合适的:
def getPositions(x:Int, y:Int, r:Int) = Iterator.range(y - r, y + r + 1) flatMap {
yr => Iterator.range(x - r, x + r + 1) map {
xr => (xr, yr)
}
} filter (_ != (x, y))
这将返回一个Iterator
,这可以通过使用next
迭代。检查结束使用hasNext
。
您可以替换Iterator
或List
或类似的东西Stream
并获得完全生成集合。
现在,让我们假设你想有一个螺旋开始在中心和移动一次一个位置。我们可以像这样的东西做到这一点:
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
}
下面是周围的边缘行走的流。
假定输入(3,3),2给出
{(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))
不隶属于 StackOverflow