Я пытаюсь реализовать функцию, которая извлекает из строки список «заполнителей». Символ-разделитель для начального и конечного заполнителей - $. У меня есть несколько идей, как реализовать его с помощью var для включения / отключения накопления, я пытаюсь реализовать его без каких-либо var
Для строки вроде
val stringToParse = "ignore/me/$aaa$/once-again/ignore/me/$bbb$/still-to-be/ignored
результат должен быть
Seq("aaa", "bbb")
какие-нибудь намеки?
Вот решение с использованием var
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> 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)
Я представлю вам два решения. Первый - это самый прямой перевод того, что вы сделали. В Scala слово accumulate
обычно переводится как вариант fold
или reduce
.
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
}
Для ясности: в этом примере вы хотите, чтобы все строки находились между совпадающими символами $? Можете ли вы опубликовать код, который у вас есть в настоящее время, чтобы мы имели некоторое представление о том, в каком направлении вы начали?