2011-02-04 73 views
4

我正在研究一个Polygon类,它在Array[Vec2]中保存了一个顶点数组(Vec2是一个简单的case类,定义了x和y )。现在寻找一个scala-esque方法迭代通过访问“next”元素的列表

,我想实现的功能在Array[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数组中的下一个项目

我已阅读IterableLike中定义的sliding方法,但似乎是非旋转的,即它不会考虑第一个项目在最后一个项目之后,因此不会返回它。

那么对此有什么好的“scala-esque”方法呢?

+0

你能澄清一两件事:你有一组点定义的多边形,所有的谎言在一些平面,它们都隐含一致绕组(即列表连接[ A,B,C]顶点实际上有一个三角形,(BA)和(CA)(归一化)的叉积产生垂直于平面的单位矢量。这是正确的吗? – 2011-02-04 08:28:39

+0

是的,这是正确的。注意我在二维空间中工作,所以我们甚至不必去谈它的正常情况(当然,除非这有助于找到解决方案)。 – pdinklag 2011-02-04 08:42:29

回答

10

当然你也可以使用sliding

(vertices :+ vertices.head) sliding 2 
0

可能的解决方案可能不是尝试访问下一个元素,而是保留前一个元素。所以,你可以采取foldLeft您的需求 - 让边缘数组的一个切片,而不第一要素,把第一个元素作为起始值和未来是这样的:

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)

+0

这将允许我将每个顶点连接到下一个顶点,但连接(5,0)将不足。 – pdinklag 2011-02-04 09:13:14

1

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

的第一个任务(和一个你似乎与挣扎)最多(在Haskell)如下处理:

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

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

这是你想要做的吗?

编辑:添加了一个例子

+0

不确定是否诚实,我将Haskell源代码翻译成Scala有点麻烦......我是函数式编程的新手,不知道Haskell的语法 - 但是如果能为我分解它,我仍然会很感激? – pdinklag 2011-02-04 09:15:03

+0

当然(我不认识斯卡拉,所以请原谅我)。 foldr是一个右联合折叠列表。我们传给它的函数被赋予一个索引x,并产生边(x,(x + 1)%4),其中%是模运算的缩写(加法“绕回”4)。然后它把这个边缘包含在结果列表中。因此,给定一个从0到3的顶点索引列表,我们计算相应的边缘。 – 2011-02-04 09:26:41

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

如果要避免复制整个列表中Debilski的解决方案,你可以做稍微详细:

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

这产生了一个视图序列上的迭代器的视图s o不要感到惊讶。

1

带zip的另一种方法。这次使用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))