Какой хороший и функциональный способ поменять элементы коллекции в Scala? - PullRequest
14 голосов
/ 26 июля 2010

В моем проекте продолжает появляться один общий случай использования. В какой-то момент у меня есть какая-то сортированная коллекция (List, Seq и т. Д ... не имеет значения) и один элемент этой коллекции. Я хочу поменять местами данный элемент со следующим (если этот элемент существует) или иногда с предыдущим.

Я хорошо знаю, как этого добиться, используя методы процедурного программирования. У меня вопрос, что было бы хорошим способом решить проблему с помощью функционального программирования (в Scala)?


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

Ответы [ 5 ]

10 голосов
/ 26 июля 2010

Ниже приведена функциональная версия обмена со следующим элементом в списке, вы просто создаете новый список с замененными элементами.

def swapWithNext[T](l: List[T], e : T) : List[T] = l match {
  case Nil => Nil
  case `e`::next::tl => next::e::tl
  case hd::tl => hd::swapWithNext(tl, e)
}
8 голосов
/ 27 июля 2010

A zipper - это чисто функциональная структура данных с указателем на эту структуру.Другими словами, это элемент с контекстом в некоторой структуре.

Например, библиотека Scalaz предоставляет класс Zipper, который моделирует список с определенным элементом списка вfocus.

Вы можете получить застежку-молнию для списка, ориентированного на первый элемент.

import scalaz._
import Scalaz._

val z: Option[Zipper[Int]] = List(1,2,3,4).toZipper

Вы можете переместить фокус застежки-молнии с помощью методов, например, на ZipperВы можете перейти к следующему смещению от текущего фокуса.

val z2: Option[Zipper[Int]] = z >>= (_.next)

Это похоже на List.tail за исключением того, что оно запоминает, где оно было.

Затем, как только вы выбрали выбранный элементв фокусе вы можете изменять элементы вокруг фокуса.

val swappedWithNext: Option[Zipper[Int]] =
  for (x <- z2;
       y <- x.delete)
    yield y.insertLeft(x.focus)

Примечание: это с последней ствольной платформой Scalaz, в которой ошибка с хвостовой рекурсивностью методов Zipper find и move имеетбыл исправлен.

Метод, который вы хотите, это просто:

def swapWithNext[T](l: List[T], p: T => Boolean) : List[T] = (for {
  z <- l.toZipper
  y <- z.findZ(p)
  x <- y.delete
} yield x.insertLeft(y.focus).toStream.toList) getOrElse l

Это соответствует элементу на основе предиката p.Но вы можете пойти дальше и рассмотреть все близлежащие элементы.Например, для реализации вставки сортировки.

5 голосов
/ 26 июля 2010

Общая версия Landei's:

import scala.collection.generic.CanBuildFrom
import scala.collection.SeqLike
def swapWithNext[A,CC](cc: CC, e: A)(implicit w1: CC => SeqLike[A,CC],
                                        w2: CanBuildFrom[CC,A,CC]): CC = {
    val seq: SeqLike[A,CC] = cc
    val (h,t) = seq.span(_ != e)
    val (m,l) = (t.head,t.tail)
    if(l.isEmpty) cc
    else (h :+ l.head :+ m) ++ l.tail
}

некоторые использования:

scala> swapWithNext(List(1,2,3,4),3)
res0: List[Int] = List(1, 2, 4, 3)

scala> swapWithNext("abcdef",'d')
res2: java.lang.String = abcedf

scala> swapWithNext(Array(1,2,3,4,5),2)
res3: Array[Int] = Array(1, 3, 2, 4, 5)

scala> swapWithNext(Seq(1,2,3,4),3)
res4: Seq[Int] = List(1, 2, 4, 3)

scala>
1 голос
/ 26 июля 2010

Альтернативная реализация метода Венечки:

def swapWithNext[T](l: List[T], e: T): List[T] = {
  val (h,t) = l.span(_ != e)
  h ::: t.tail.head :: e :: t.tail.tail
}

Обратите внимание, что это не с ошибкой, если е является последним элементом.

Если вы знаете оба элемента, и каждый элемент встречается только один раз, он становится более элегантным:

def swap[T](l: List[T], a:T, b:T) : List[T] = l.map(_ match {
  case `a` => b
  case `b` => a
  case e => e }
)
0 голосов
/ 11 августа 2015

Как насчет:

  val identifierPosition = 3;
  val l = "this is a identifierhere here";
  val sl = l.split(" ").toList;

  val elementAtPos = sl(identifierPosition)
  val swapped = elementAtPos :: dropIndex(sl , identifierPosition)

  println(swapped)

  def dropIndex[T](xs: List[T], n: Int) : List[T] = {
    val (l1, l2) = xs splitAt n
    l1 ::: (l2 drop 1)
  }

слава http://www.scala -lang.org / old / node / 5286 для функции dropIndex

...