如何在Scala中对大小写字符串进行排序

3
在Scala中有一个字符串列表。假设这些字符串只包含英文字母(大写和小写)。下面是一个示例列表:
val l1 = List("ab","aa", "bc","Aa", "Ab", "Ba", "BB")

当我们使用以下代码对其进行排序时:
l1.sortWith(_ < _)  

我们将会收到:
List(Aa, Ab, BB, Ba, aa, ab, bc)

因此,这种排序使用字母之间的以下关系:

A < B < C < ... < a < b < c ...

我们还可以使用以下方式:
l1.sortWith(_.toLowerCase < _.toLowerCase)

接收:

List(aa, Aa, ab, Ab, Ba, BB, bc)

现在字母之间的关系如下:

(a=A) < (b=B) < (c=C) ...

但是如何在Scala中按照以下字母顺序对它们进行排序呢?:
a < A < b < B < c < C ...

因此,结果应该是:
List(aa, ab, Aa, Ab, bc, Ba, BB)
4个回答

5
scala> def compareChar(c1:Char, c2:Char) = {
         if ( c1 == c2 ) None
         else if (c1.toLower == c2.toLower) Some(c2.isUpper)
         else Some(c1.toLower < c2.toLower)
       }
compareChar: (c1: Char, c2: Char)Option[Boolean]

scala> def compareString(s1:String, s2:String) : Boolean = {
         (s1 zip s2).collectFirst {
           case (c1,c2) if (compareChar(c1,c2).isDefined) => compareChar(c1,c2).get
         }.getOrElse(s1.length < s2.length)
       }
compareString: (s1: String, s2: String)Boolean

scala> l1 sortWith compareString
res02: List[String] = List(aa, ab, Aa, Ab, bc, Ba, BB)

编辑: 内联版本:

def compareString(s1:String, s2:String) : Boolean = {
  (s1 zip s2).collectFirst {
    case (c1, c2) if c1 == c2 => compareString(s1.tail, s2.tail)
    case (c1, c2) if c1.toLower == c2.toLower => c2.isUpper // same letter, different case, uppercase wins
    case (c1, c2) => c1.toLower < c2.toLower
  }.getOrElse(s1.length < s2.length) // same prefix, the longest string is bigger
}

scala> val l1 = List("ab","aa", "bc","Aa", "Ab", "Ba", "BB")
l1: List[String] = List(ab, aa, bc, Aa, Ab, Ba, BB)

scala> l1 sortWith compareString
res0: List[String] = List(aa, ab, Aa, Ab, bc, Ba, BB)

scala> List("ABC","AB") sortWith compareString
res1: List[String] = List(AB, ABC)

List("ABC","AB") sortWith compareString 是意料之外的。在 case 块中,我会内联 compareChar。在 if 守卫中不需要括号。另外,如果您可以直接贴出 REPL 的剪贴板,则更好复制粘贴。 - som-snytt
@som-snytt: 没错,我修复了“相同前缀/不同长度”的错误。同时,我使用“inlined compareChar”版本更新了答案(实际上比我的第一个版本容易阅读得多)。 - Marth

2
scala> import math.Ordering.Implicits._
import math.Ordering.Implicits._

scala> val words = List("ab","aa", "bc","Aa", "Ab", "Ba", "BB")
words: List[String] = List(ab, aa, bc, Aa, Ab, Ba, BB)

scala> words sortBy (_ map (c => if (c.isUpper) 2 * c + 1 else 2 * (c - ('a' - 'A'))))
res0: List[String] = List(aa, ab, Aa, Ab, bc, Ba, BB)

0

试试这个

  val l1 = List("ab","aa", "bc","Aa", "Ab", "Ba", "BB")
  def comp[T <: String](a: T, b: T) = {
    def _comp(i: Int): Boolean = try {
      val (l, r) = (a(i), b(i))
      if (l == r) _comp(i+1) else l.toLower < r.toLower
    } catch {
      case e: IndexOutOfBoundsException => true
    }
    _comp(0)
  }
  println(l1.sortWith(comp)) // List(aa, ab, Aa, Ab, bc, Ba, BB)

0

tailrec 优化的解决方案:

def comp(x: String, y: String): Boolean = {
  @tailrec
  def go(xs: List[Char], ys: List[Char]): Boolean = {
    (xs, ys) match {
      case (hx :: tx, hy :: ty) =>
        if (hx == hy) go(tx, ty)
        else if (hx.toLower == hy.toLower) hx.isLower
        else if (hx.isLower) hx < hy
        else hx < hy.toUpper
      case (Nil, _) => true
      case (_, Nil) => false
    }
  }
  go(x.toList, y.toList)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接