【问题标题】:Scala conditional accumulationScala条件累加
【发布时间】:2019-03-02 18:40:01
【问题描述】:

我正在尝试实现一个从给定字符串中提取由 $ 字符分隔的“占位符”的函数。

处理字符串:

val stringToParse = "ignore/me/$aaa$/once-again/ignore/me/$bbb$/still-to-be/ignored

结果应该是:

Seq("aaa", "bbb")

以下使用var 切换累积的实现的Scala 惯用替代方案是什么?

import fiddle.Fiddle, Fiddle.println
import scalajs.js
import scala.collection.mutable.ListBuffer

@js.annotation.JSExportTopLevel("ScalaFiddle")
object ScalaFiddle {
  // $FiddleStart
  
  
  val stringToParse = "ignore/me/$aaa$/once-again/ignore/me/$bbb$/still-to-be/ignored"
  
  class StringAccumulator {
    
    val accumulator: ListBuffer[String] = new ListBuffer[String]
    val sb: StringBuilder = new StringBuilder("")
    var open:Boolean = false
    
    def next():Unit = {
      if (open) {
        accumulator.append(sb.toString)
        sb.clear
        open = false
      } else {
        open = true
      }
    }
    
    def accumulateIfOpen(charToAccumulate: Char):Unit = {
      if (open) sb.append(charToAccumulate)
    }
    
    def get(): Seq[String] = accumulator.toList
  }
  
  def getPlaceHolders(str: String): Seq[String] = {
    val sac = new StringAccumulator
  
    str.foreach(chr => {
      if (chr == '$') {
        sac.next()
      } else {
        sac.accumulateIfOpen(chr)
      }
    })
    
    sac.get
  }
  
  println(getPlaceHolders(stringToParse))
  // $FiddleEnd
}

【问题讨论】:

  • 要清楚,在这个例子中,你希望所有字符串都在匹配的 $?您能否发布您目前拥有的代码,以便我们了解您开始的方向?
  • 我已经用一个使用 var 的例子更新了这个问题

标签: scala


【解决方案1】:

我将向您介绍两种解决方案。第一个是你所做的最直接的翻译。在 Scala 中,如果你听到 accumulate 这个词,它通常会转换为 foldreduce 的变体。

 def extractValues(s: String) =
 {
    // We can combine the functionality of your boolean and StringBuilder by using an Option
    s.foldLeft[(ListBuffer[String],Option[StringBuilder])]((new ListBuffer[String], Option.empty))
                  {
                    //As we fold through, we have the accumulated list, possibly a partially built String and the current letter
                    case ((accumulator,sbOption),char) =>
                    {
                      char match
                      {
                        //This logic pretty much matches what you had, adjusted to work with the Option
                        case '$' =>
                        {
                          sbOption match
                          {
                            case Some(sb) =>
                            {
                              accumulator.append(sb.mkString)
                              (accumulator,None)
                            }
                            case None =>
                            {
                              (accumulator,Some(new StringBuilder))
                            }
                          }
                        }
                        case _ =>
                        {
                          sbOption.foreach(_.append(char))
                          (accumulator,sbOption)
                        }
                      }
                    }
                  }._1.map(_.mkString).toList
 }

但是,这似乎很复杂,因为听起来应该是一项简单的任务。我们可以使用正则表达式,但它们很可怕,所以让我们避免它们。其实稍微想一想这个问题其实就很简单了。

 def extractValuesSimple(s: String) =
 {
    s.split('$'). //Split the string on the $ character
      dropRight(1). //Drops the rightmost item, to handle the case with an odd number of $
      zipWithIndex.filter{case (str, index) => index % 2 == 1}. //Filter out all of the even indexed items, which will always be outside of the matching $
      map{case (str, index) => str}.toList //Remove the indexes from the output
 }

【讨论】:

    【解决方案2】:

    这个解决方案够吗?

    scala> val stringToParse = "ignore/me/$aaa$/once-again/ignore/me/$bbb$/still-to-be/ignored"
    stringToParse: String = ignore/me/$aaa$/once-again/ignore/me/$bbb$/still-to-be/ignored
    
    scala> val P = """\$([^\$]+)\$""".r
    P: scala.util.matching.Regex = \$([^\$]+)\$
    
    scala> P.findAllIn(stringToParse).map{case P(s) => s}.toSeq
    res1: Seq[String] = List(aaa, bbb)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-11
      • 2022-01-23
      • 1970-01-01
      • 2019-07-03
      • 2016-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多