Ищу подход scala-esque для перебора списка с доступом к «следующему» элементу - PullRequest
4 голосов
/ 04 февраля 2011

Я работаю над классом 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? */)
}

Проблема в том,что нет способа получить доступ к элементу next в массиве, учитывая текущий элемент v.

Я читал о методе sliding, определенном в IterableLike, однако этокажется, что он не вращается, то есть он не будет считать первый элемент последующим за последним элементом и, следовательно, не будет возвращать его.

Так что же является хорошим подходом "scala-esque" к этому?

Ответы [ 6 ]

10 голосов
/ 04 февраля 2011

Конечно, вы можете использовать sliding:

(vertices :+ vertices.head) sliding 2
4 голосов
/ 04 февраля 2011
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))
1 голос
/ 04 февраля 2011

Еще один способ сделать это с помощью 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))
1 голос
/ 04 февраля 2011

Можно (а) перечислить все (направленные) ребра (определенные индексами для их вершин), (б) дать список ребер и список вершин, чтобы построить массив отрезков (выполнив поиск ). Обе задачи, я думаю, могут быть выполнены без мутаций.

Первая задача (и та, с которой вы, похоже, боретесь) может быть решена следующим образом (в Haskell):

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

Осталось обобщить этот пример.

Это то, что вы хотите сделать?

РЕДАКТИРОВАТЬ: добавил пример

0 голосов
/ 04 февраля 2011

Если вы хотите избежать копирования всего списка, как в решении Дебилски, вы можете сделать несколько подробное:

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

Это дает представление итератора по последовательности представлений, поэтому не удивляйтесь.

0 голосов
/ 04 февраля 2011

Возможное решение - возможно, не пытаться получить доступ к следующему элементу, а сохранить прежний. Таким образом, вы можете принять 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)

...