2010-07-26 14 views
14

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.¿Cuál es una forma buena y funcional de intercambiar elementos de colección en Scala?

Soy muy consciente de las formas de lograrlo utilizando 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. 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.

+0

¿Has mirado en esto? http://rosettacode.org/wiki/Generic_swap#Scala –

Respuesta

10

La siguiente es la versión funcional de swap con el siguiente elemento de 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) 
} 
+0

su respuesta hace exactamente lo que pretendía. gracias por recordarme el patrón de mecanizado. – aeisele

1

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 no funciona con un error si e es el último elemento.

Si conoces a ambos elementos, y cada elemento se produce sólo 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 } 
) 
5

Una versión genérica de Landei de:

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

+1 para tipos de orden superior :-) – Landei

8

A cremallera es una estructura de datos funcional pura w 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 obtener una lista, centrada en el primer elemento.

import scalaz._ 
import Scalaz._ 

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

se puede mover el foco de la cremallera utilizando métodos de Zipper, por ejemplo, se puede pasar al siguiente desplazamiento desde el enfoque actual.

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

Esto es como List.tail excepto que recuerda donde ha estado.

Luego, una vez que tenga 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 Scalaz tronco, en el que un error con find y move métodos recursiva de cola de la cremallera se ha solucionado.

El método que desea es entonces 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.

+0

Muy buena explicación de Zipper. Gracias. Siempre me he preguntado qué pasa con esto. ;) – aeisele

+0

Sería bueno si pudiera proporcionar la función completa que toma una colección, y un elemento, y cambia la primera aparición de ese elemento en la colección con la siguiente. –

0

¿Qué tal:

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

Cuestiones relacionadas