寻求一种scala-esque方法来遍历一个可以访问“next”元素的列表

时间:2011-02-04 07:56:25

标签: scala functional-programming scala-2.8

我正在处理一个Polygon类,它在Array[Vec2]中保存一个顶点数组(Vec2是一个定义x和y的简单大小写类。)

现在,我想实现一个函数来返回Array[LineSegment]中多边形的边缘(其中LineSegment也是一个定义开始和结束的简单案例类)。

解决方案是创建将每个顶点连接到数组中的下一个顶点的线段,最后将最后一个顶点连接到第一个顶点。

我只习惯于命令式编程,所以这是我的必要方法:

def edges: Array[LineSegment] = {
  val result = new Array[LineSegment](vertices.length)

  for (i <- 0 to vertices.length - 2) {
    result.update(i, LineSegment(vertices.apply(i), vertices.apply(i + 1)))
  }
  result.update(edges.length - 1, LineSegment(vertices.head, vertices.last))

  result
}

这很好用,但很简单。我想在这里使用函数式编程的优点,但我有点坚持。

我的想法是把它写成类似的东西:

def edges: Array[LineSegment] = {
    for (v <- vertices) yield 
      LineSegment(v, if (v == vertices.last) vertices.head else /* next? */)
}

问题是,在给定当前项v的情况下,无法访问数组中的 next 项。

我已经阅读了sliding中定义的IterableLike方法,但这似乎是非旋转的,即它不会考虑最后一项之后的第一项,因此不会把它归还。

那么什么是一个好的“scala-esque”方法呢?

6 个答案:

答案 0 :(得分:10)

当然,您可以使用sliding

(vertices :+ vertices.head) sliding 2

答案 1 :(得分:4)

def cyclicSliding[A](s:Seq[A]) = s.zip(s.tail :+ s.head)

println(cyclicSliding(List(1,2,3,4)))
//--> List((1,2), (2,3), (3,4), (4,1))

答案 2 :(得分:1)

有可能(a)枚举所有(有向)边(由其顶点的索引定义),(b)给定边列表和顶点列表,以构造线段数组(通过执行查找) )。我认为,这两项任务都可以在没有变异的情况下完成。

第一个任务(以及你似乎正在努力解决的任务)可以按如下方式处理(在Haskell中):

foldr (\x a -> (x,(x+1) `mod` 4):a) [] [0..3]

剩下的就是概括这个例子。

这是你想要做的吗?

编辑:添加了一个例子

答案 3 :(得分:1)

拉链的另一种方式。这次使用zipAll:

val l1 = List(1,2,3,4)
l1 zipAll (l1.tail,null,l1.head)
res: List((1,2), (2,3), (3,4), (4,1))

答案 4 :(得分:0)

可能的解决方案可能不是尝试访问下一个元素,而是保持流行的元素。因此,您可以根据需要采用foldLeft - 获取一个没有第一个元素的edge数组,将第一个元素作为起始值,接下来是这样的:

val n = 5
(0 to n).toList.slice(1, n + 1).foldLeft(0)((x, y) => {print(x,y); y})

输出:(0,1)(1,2)(2,3)(3,4)(4,5)

答案 5 :(得分:0)

如果你想避免像在Debilski的解决方案中那样复制整个列表,你可以稍微详细一点:

vertices.view.sliding(2).map(p => if (p.size == 1) p :+ vertices.head else p)

这会在一系列视图上产生一个迭代器视图,所以不要感到惊讶。