寻求一种 scala 式的方法来迭代列表并访问“下一个”元素
我正在开发一个 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
的情况下,无法访问数组中的下一个项。
我读过关于 IterableLike 中定义的 sliding
方法,但这似乎是非旋转的,即它不会认为第一个项目是最后一个项目的后续项目因此不退货。
那么什么是好的“scala 式”方法呢?
I am working on a Polygon class which holds an array of vertices in an Array[Vec2]
(with Vec2
being a simple case class defining x and y).
Now, I would like to implement a function to return the edges of the polygon in an Array[LineSegment]
(where LineSegment is again a simple case class which defines start and end).
The solution is to create line segments connecting each vertex to the next in the array, and finally connecting the last vertex to the first.
I'm only used to imperative programming, so this is my imperative approach:
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
}
This works fine, but it's plain ugly. I want to use the advantages of functional programming here, but I'm kinda stuck with that.
My idea was to put it like something similar to this:
def edges: Array[LineSegment] = {
for (v <- vertices) yield
LineSegment(v, if (v == vertices.last) vertices.head else /* next? */)
}
The problem is that there's no way to access the next item in the array given the current item v
.
I've read about the sliding
method defined in IterableLike
, however that seems to be non-rotating, ie it will not consider the first item to be subsequent to the last item and therefore not return it.
So what is a good "scala-esque" approach to this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
当然你可以使用
滑动
:Of course you can use
sliding
:可以(a)枚举所有(有向)边(由其顶点的索引定义),(b)给定边列表和顶点列表,以构造线段数组(通过查找)。我认为这两项任务都可以在不发生突变的情况下完成。
第一个任务(以及你似乎正在努力解决的任务)可以按如下方式处理(在 Haskell 中):
剩下的就是概括这个例子。
这是你想做的吗?
编辑:添加了一个示例
It is possible to (a) enumerate all (directed) edges (defined by indices to their vertices), (b) given a list of edges and a list of vertices, to construct an array of line segments (by doing lookups). Both tasks, I think, can be accomplished without mutation.
The first task (and the one you seem to be struggling with) can be dealt with as follows (in Haskell):
What is left is to generalize this example.
Is this what you want to do?
EDIT: added an example
另一种方法是使用 zip。这次使用 zipAll:
One more way to it with zip. This time using zipAll:
可能的解决方案可能不是尝试访问下一个元素,而是保留前一个元素。因此,您可以根据需要采用
foldLeft
- 获取没有第一个元素的边数组切片,将第一个元素作为起始值,然后执行以下操作:输出:(0,1) (1,2) (2,3) (3,4) (4,5)
The possible solution is maybe not to try to access the next element, but to keep the prevous one. So, you can adopt a
foldLeft
to your needs - get a slice of edges array without the first element, put the first element as a starting value and next something like this:Output: (0,1) (1,2) (2,3) (3,4) (4,5)
如果您想避免像 Debilski 的解决方案中那样复制整个列表,您可以执行稍微冗长的操作:
这会在一系列视图上的迭代器上生成一个视图,因此不要感到惊讶。
If you want to avoid copying the whole list as in Debilski's solution, you can do the slightly verbose:
This yields a view on an iterator over a sequence of views, so don't be surprised.