【问题标题】:What's a good and functional way to swap collection elements in Scala?在 Scala 中交换集合元素的好方法是什么?
【发布时间】:2011-03-21 02:32:39
【问题描述】:

在我的一个项目中,一个常见的用例不断出现。在某些时候,我有某种排序的集合(列表、序列等...无关紧要)和这个集合的一个元素。我想要做的是将给定元素与它的下一个元素(如果此元素存在)或有时与前一个元素交换。

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


谢谢大家的回答。我接受了我自己最了解的那个。由于我(还)不是函数式程序员,所以我很难决定哪个答案是真正最好的。在我看来,它们都非常好。

【问题讨论】:

标签: scala functional-programming swap scala-collections


【解决方案1】:

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

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)
}

【讨论】:

  • 您的回答完全符合我的意图。谢谢你提醒我模式匹配。
【解决方案2】:

zipper 是一个纯函数式数据结构,带有指向该结构的指针。换句话说,它是一个在某种结构中具有上下文的元素。

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

你可以获得一个列表的拉链,专注于第一个元素。

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 的尾递归 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

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

【讨论】:

  • 对拉链的很好解释。谢谢你。我一直想知道这些是什么。 ;)
  • 如果你能提供一个完整的函数来接受一个集合和元素,并将集合中该元素的第一次出现与下一个交换,那就太好了。
【解决方案3】:

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
}

一些用法:

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>

【讨论】:

    【解决方案4】:

    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
    }
    

    请注意,如果 e 是最后一个元素,则此操作会失败并出现错误。

    如果你知道这两个元素,并且每个元素只出现一次,它会变得更加优雅:

    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】:

      怎么样:

        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 函数

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-06-27
        • 1970-01-01
        • 2015-03-22
        • 2017-01-03
        • 2011-08-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多