guide - ¿Cuál es una forma buena y funcional de intercambiar elementos de colección en Scala?
scala guide style (5)
En un proyecto mío, un caso de uso común sigue apareciendo. En algún momento obtuve una colección ordenada de algún tipo (List, Seq, etc ... no importa) y un elemento de esta colección. Lo que quiero hacer es intercambiar el elemento dado con su siguiente elemento (si este elemento existe) o en algún momento con el elemento anterior.
Estoy muy consciente de las formas de lograr esto usando técnicas de programación de procedimientos. Mi pregunta es ¿cuál sería una buena manera de resolver el problema mediante la programación funcional (en Scala)?
Gracias a todos por sus respuestas o comentarios. Acepté el que yo mismo entendí más. Como no soy un programador funcional (todavía), es un poco difícil para mí decidir qué respuesta fue realmente la mejor. Todos son bastante buenos en mi opinión.
La siguiente es la versión funcional de swap con el siguiente elemento en una lista, usted acaba de construir una nueva lista con elementos intercambiados.
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)
}
Qué tal si :
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)
}
Felicitaciones a http://www.scala-lang.org/old/node/5286 para la función dropIndex
Una cremallera es una estructura de datos funcional pura con un puntero en esa estructura. Dicho de otra manera, es un elemento con un contexto en alguna estructura.
Por ejemplo, la biblioteca Scalaz proporciona una clase Zipper
que modela una lista con un elemento particular de la lista en foco.
Puede obtener una cremallera para una lista, enfocada en el primer elemento.
import scalaz._
import Scalaz._
val z: Option[Zipper[Int]] = List(1,2,3,4).toZipper
Puede mover el enfoque de la cremallera usando métodos en Zipper
, por ejemplo, puede pasar al siguiente desplazamiento desde el enfoque actual.
val z2: Option[Zipper[Int]] = z >>= (_.next)
Esto es como List.tail
excepto que recuerda dónde ha estado.
Luego, una vez que tiene el elemento elegido en foco, puede modificar los elementos alrededor del foco.
val swappedWithNext: Option[Zipper[Int]] =
for (x <- z2;
y <- x.delete)
yield y.insertLeft(x.focus)
Nota: esto es con la última cabeza de baúl de Scalaz, en la que se ha corregido un error con los métodos de find
y move
recursivo de cola de Zipper.
El método que desea es simplemente:
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
Esto coincide con un elemento basado en un predicado p
. Pero puedes ir más allá y considerar todos los elementos cercanos también. Por ejemplo, para implementar una ordenación por inserción.
Una implementación alternativa para el método de venechka:
def swapWithNext[T](l: List[T], e: T): List[T] = {
val (h,t) = l.span(_ != e)
h ::: t.tail.head :: e :: t.tail.tail
}
Tenga en cuenta que esto falla con un error si e es el último elemento.
Si conoce ambos elementos, y cada elemento aparece solo una vez, se vuelve más elegante:
def swap[T](l: List[T], a:T, b:T) : List[T] = l.map(_ match {
case `a` => b
case `b` => a
case e => e }
)
Una versión genérica de Landei:
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
}
algunos usos:
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>