在Scala中交换集合元素的好方法是什么?

And*_*ele 14 swap functional-programming scala scala-collections

在我的一个项目中,一个常见的用例不断涌现.在某些时候,我有一个某种类型的排序集合(List,Seq等......无所谓)和这个集合的一个元素.我想要做的是交换给定元素与它的后续元素(如果此元素存在)或在某些时候与前面的元素交换.

我很清楚使用过程编程技术实现这一目标的方法.我的问题是通过函数式编程(在Scala中)解决问题的好方法是什么?


谢谢大家的答案.我接受了我自己最了解的那个.由于我不是一名功能性程序员(但是),我很难确定哪个答案真的是最好的.在我看来,它们都非常好.

ven*_*hka 10

以下是与列表中的下一个元素交换的功能版本,您只需构造一个包含元素交换的新列表.

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)
}
Run Code Online (Sandbox Code Playgroud)


Apo*_*isp 8

拉链是纯功能数据结构的指针到该结构.换句话说,它是一个在某种结构中具有上下文的元素.

例如,Scalaz库提供了一个Zipper类,该类使用列表中的特定元素对列表进行建模.

您可以获得列表的拉链,重点放在第一个元素上.

import scalaz._
import Scalaz._

val z: Option[Zipper[Int]] = List(1,2,3,4).toZipper
Run Code Online (Sandbox Code Playgroud)

您可以使用方法移动拉链的焦点Zipper,例如,您可以移动到当前焦点的下一个偏移.

val z2: Option[Zipper[Int]] = z >>= (_.next)
Run Code Online (Sandbox Code Playgroud)

这就像List.tail它记得它曾经存在的地方一样.

然后,一旦您将所选元素置于焦点上,就可以修改焦点周围的元素.

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

注意:这是最新的Scalaz主干头,其中修复了Zipper的尾递归findmove方法的错误.

你想要的方法就是:

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
Run Code Online (Sandbox Code Playgroud)

这匹配基于谓词的元素p.但你可以进一步考虑所有附近的元素.例如,要实现插入排序.


Eas*_*sun 5

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
}
Run Code Online (Sandbox Code Playgroud)

一些用法:

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>
Run Code Online (Sandbox Code Playgroud)