String 将字符串拆分为组

String 将字符串拆分为组,string,scala,String,Scala,我试图将字符串“分组”成段,我想这个例子会更简洁地解释它 scala> val str: String = "aaaabbcddeeeeeeffg" ... (do something) res0: List("aaaa","bb","c","dd","eeeee","ff","g") 我可以想出一些方法,以命令式的方式使用vars来实现这一点,并通过字符串查找组,但我想知道是否有更好的功能解决方案可以实现 达到什么程度?我一直在浏览Scala API,但似乎没有适合我需要的东西 任何帮

我试图将字符串“分组”成段,我想这个例子会更简洁地解释它

scala> val str: String = "aaaabbcddeeeeeeffg"
... (do something)
res0: List("aaaa","bb","c","dd","eeeee","ff","g")
我可以想出一些方法,以命令式的方式使用vars来实现这一点,并通过字符串查找组,但我想知道是否有更好的功能解决方案可以实现 达到什么程度?我一直在浏览Scala API,但似乎没有适合我需要的东西


任何帮助都将不胜感激

您可以使用span递归拆分字符串:

def s(x : String) : List[String] = if(x.size == 0) Nil else {
    val (l,r) = x.span(_ == x(0))
    l :: s(r) 
}
尾部递归:

@annotation.tailrec def s(x : String, y : List[String] = Nil) : List[String] = {
    if(x.size == 0) y.reverse 
    else {
        val (l,r) = x.span(_ == x(0))
        s(r, l :: y)
    }
}
编辑:尾部递归版本:

def group(s: String, result: List[String] = Nil): List[String] = s match {
  case "" => result reverse
  case s  => group(s.dropWhile(_==s.head), s.takeWhile(_==s.head) :: result)
}

可以像其他参数一样使用,因为第二个参数有一个默认值,因此不必提供。

您可以使用以下帮助器函数:

val str = "aaaabbcddddeeeeefff"

def zame(chars:List[Char]) = chars.partition(_==chars.head)

def q(chars:List[Char]):List[List[Char]] = chars match {
    case Nil => Nil
    case rest =>
        val (thesame,others) = zame(rest)
        thesame :: q(others)
}

q(str.toList) map (_.mkString)
这应该能奏效,对吧?毫无疑问,它可以被清理成一个衬里,甚至更进一步

使其成为一个衬里:

scala>  val str = "aaaabbcddddeeeeefff"
str: java.lang.String = aaaabbcddddeeeeefff

scala> str.groupBy(identity).map(_._2)
res: scala.collection.immutable.Iterable[String] = List(eeeee, fff, aaaa, bb, c, dddd)
更新:

正如@Paul提到的订单,这里是更新版本:

scala> str.groupBy(identity).toList.sortBy(_._1).map(_._2)
res: List[String] = List(aaaa, bb, c, dddd, eeeee, fff)
编辑:必须仔细阅读。下面没有功能代码

有时,一些可变状态有助于:

def group(s : String) = {
  var tmp = ""
  val b = Seq.newBuilder[String]

  s.foreach { c =>
    if ( tmp != "" && tmp.head != c ) {
      b += tmp
      tmp = ""
    }

    tmp += c
  }
  b += tmp

  b.result
}
if段的运行时长度最多为常量,tmp.+=可能会产生最大的开销。在上的严格运行时使用字符串生成器

group("aaaabbcddeeeeeeffg")
> Seq[String] = List(aaaa, bb, c, dd, eeeeee, ff, g)

似乎所有其他答案都集中在收集操作上。但纯字符串+正则表达式解决方案要简单得多:

str split """(?<=(\w))(?!\1)""" toList

在这个正则表达式中,我对捕获的字符使用正向向后看和反向向前看,这是一个使用fold:

通过隐式转换对字符串执行的所有序列操作都隐藏了大量成本。我想真正的复杂性在很大程度上取决于Seq字符串转换成的类型

*Afaik集合库中的所有/大多数操作都依赖于迭代器,这是一个本质上不起作用的概念。但至少代码看起来是功能性的。

从Scala 2.13开始,构建器现在提供了列表,该列表可以与以下内容结合使用:

或者,结合Scala 2.13的构建器:

详情:

Unfold def Unfold[A,S]init:Sf:S=>选项[A,S]:列表[A]基于内部状态init,在我们的示例中,该内部状态init是使用aabbaaacdeeffg初始化的。 对于每个迭代,我们span def spanp:Char=>Boolean:String,String这个内部状态,以便找到包含相同符号的前缀,并生成一个包含前缀和字符串其余部分的字符串、字符串元组。span在这种情况下非常幸运,因为它生成的正是unfold所期望的:一个元组,包含列表的下一个元素和新的内部状态。 当内部状态为时,展开停止,在这种情况下,我们将不产生展开退出所期望的结果。
如果要使用scala API,可以使用内置函数:

str.groupBy(c => c).values
或者,如果您介意将其排序并放入列表中:

str.groupBy(c => c).values.toList.sorted

如果您能提及并标记您想使用的语言,这将非常有用!这篇文章被贴上标签了吗?可能需要一段时间才能出现在SO服务器上或其他什么东西上,您希望与aaabbcddeffffffhhhhiiiiijjjj等匹配吗?还是仅仅这7个字符?对你来说,建设者的功能足够吗?当然,分区在本质上是可变的,但由于它是标准库的一部分,所以应该是值得信任的。我已经见过这个问题,仅Scala一个问题,已经有两三次了。我认为分区并不能满足需要。给定aaabbbbaa,它将返回aaaabbbbb partition将列表一分为二,因此像这样拆分aaaabbbbaaa.toList将产生aaaa,bbbbaaaaaaa,然后以递归方式将相同的函数应用于列表的其余部分。我认为它做了您需要的事情,而不是在某一点上拆分列表。它收集与谓词scala>aaabbbaaa分区匹配或不匹配的所有元素=='a'res0:String,String=aaaaaa,bbbb scala>您要查找的函数是span see Thomas'postYes。你是对的,这里的分区是错的。没有很好地阅读文档。ThxI认为从OP的示例中可以清楚地看出,片段的顺序很重要。@Paul没有提到顺序,反正我更新了代码这将导致Listbb,aaa代表AAABBA。。。不是吗?“我不知道这是否是djhworld想要的。”马丁·林格-是的,是的,它将是AAA,bb。我想这个问题可以从很多方面来解释。我有点困惑,这个“身份”的东西是从哪里来的?谢谢你的回答,但这不是我想要的,我对字符串在第一个中的具体顺序很感兴趣place@Paul-不,我不这么认为。这场比赛会占用更多的空间,对比赛场地大小的检查也很清楚@托马斯-见我对马丁·林回答的评论。我喜欢这个答案,但我确实想指出这种方法的缺点。@Paul你可以像Martin那样实现它。我确实这样做了,但它不太可读。谢谢你的回复,我对span方法没有太多经验,所以读这样的东西给了我一些帮助
对于这个问题的解决方案,您的思维过程有着深刻的见解,因此,谢谢!有没有办法让这个尾巴递归?我遇到了大容量的堆栈溢出错误inputs@Rex克尔:为什么会在^2上?我认为字符串的每个字符都有两个比较操作,比最优的要少。更好的解决方案是像Thomas建议的那样使用span,但这仍然是开还是不开?我遗漏了什么吗?@Rex我想说,在这个实现中,每个角色都会被消耗一次,实际上是两次。这是开着的。是的,你可以把它做成尾巴递归的。这是一个练习,没有问题。@Martin,@Thomas-如果每个字符都不同,则生成n个平均长度为n/2的字符串,用于^2个工作总数。@Rex创建长度为k的子字符串是O1,不正确。看@Martin-我错了,但不是因为你所说的原因;我不认为dropWhile能够一路回到substring。但我查了来源,看起来是的。因此,只有堆栈溢出问题,而不是^2上的问题。如果collection.mutable.Seq上有某个方法实际修改了序列,那么您可以在tmp中使用一个双链接列表,并在时间和内存上。+1-非常好!我总是忘记正则表达式惊人的力量,以及惊人的不可读性。如果不仔细研究,你真的能说你理解这个吗?这个功能正常吗@保罗,这就是评论for@marcello,但我熟悉regexps。你为什么认为我不是?我认为这种用法可以算作高级regexp用法lookahead和lookbehind,并且会将大多数读者甩在后面。我认为马丁的解决方案对更多的读者来说是更容易理解的。保罗:我同意,但是考虑一下:如果你使用上面的代码为真,你会把它放进一个方法中,给它一个好名字,分裂组,文档和单元测试它。想要拆分字符串的人不必理解正则表达式,只要splitByGroup的实现者理解即可。
List.unfold("aaaabbaaacdeeffg") {
  case ""   => None
  case rest => Some(rest.span(_ == rest.head))
}
// List[String] = List("aaaa", "bb", "aaa", "c", "d", "ee", "ff", "g")
List.unfold("aaaabbaaacdeeffg") {
  rest => Option.unless(rest.isEmpty)(rest.span(_ == rest.head))
}
// List[String] = List("aaaa", "bb", "aaa", "c", "d", "ee", "ff", "g")
str.groupBy(c => c).values
str.groupBy(c => c).values.toList.sorted