Oczywiście coś budujesz, więc możesz spróbować ... budowniczego!
Podobnie jak Jürgen, moją pierwszą myślą było spasować, gdzie zbierasz wynik.
A mutable.Builder dokonuje akumulacji w sposób zbalansowany, z kolekcją.generic.CanBuildFrom, aby wskazać konstruktora, który będzie używał do utworzenia kolekcji docelowej ze zbioru źródłowego. Trzymasz to zmienne na tyle długo, aby uzyskać wynik. To jest moja wtyczka do zlokalizowanej zmienności. Nie należy zakładać, że ścieżka od List [String] do List [Project] jest niezmienna.
Do innych delikatnych odpowiedzi (tych z nieujemnymi ocenami aprecjacji) dodałbym, że styl funkcjonalny oznacza rozkład funkcjonalny i zwykle małe funkcje.
Jeśli nie korzystasz z analizatorów składni regex, nie zapomnij o wyrażeń regularnych w dopasowaniach wzorców.
I staraj się oszczędzić kropki. W rzeczywistości wierzę, że jutro jest Dzień Zapasów, a ludzie z wrażliwością na kropki powinni pozostać w domu.
case class Project(user: String, name: String, description: String)
trait Sample {
val sample = """
|User=Hans
|Project=Blow up the moon
|The slugs are going to eat the mustard. // multiline possible!
|They are sneaky bastards, those slugs.
|
|User=Bob
|I haven't thought up a project name yet.
|
|User=Greta
|Project=Burn the witch
|It's necessary to escape from the witch before
|we blow up the moon. I hope Hans sees it my way.
|Once we burn the bitch, I mean witch, we can
|wreak whatever havoc pleases us.
|""".stripMargin
}
object Test extends App with Sample {
val kv = "(.*?)=(.*)".r
def nonnully(s: String) = if (s == null) "" else s + " "
val empty = Project(null, null, null)
val (res, dummy) = ((List.empty[Project], empty) /: sample.lines) { (acc, line) =>
val (sofar, cur) = acc
line match {
case kv("User", u) => (sofar, cur copy (user = u))
case kv("Project", n) => (sofar, cur copy (name = n))
case kv(k, _) => sys error s"Bad keyword $k"
case x if x.nonEmpty => (sofar, cur copy (description = s"${nonnully(cur.description)}$x"))
case _ if cur != empty => (cur :: sofar, empty)
case _ => (sofar, empty)
}
}
val ps = if (dummy == empty) res.reverse else (dummy :: res).reverse
Console println ps
}
Mecz można puree tędy też:
val (res, dummy) = ((List.empty[Project], empty) /: sample.lines) {
case ((sofar, cur), kv("User", u)) => (sofar, cur copy (user = u))
case ((sofar, cur), kv("Project", n)) => (sofar, cur copy (name = n))
case ((sofar, cur), kv(k, _)) => sys error s"Bad keyword $k"
case ((sofar, cur), x) if x.nonEmpty => (sofar, cur copy (description = s"${nonnully(cur.description)}$x"))
case ((sofar, cur), _) if cur != empty => (cur :: sofar, empty)
case ((sofar, cur), _) => (sofar, empty)
}
Przed owczarni, wydawało się prostsze do zrobienia akapity pierwszy. Czy to imperatywne myślenie?
object Test0 extends App with Sample {
def grafs(ss: Iterator[String]): List[List[String]] = {
val (g, rest) = ss dropWhile (_.isEmpty) span (_.nonEmpty)
val others = if (rest.nonEmpty) grafs(rest) else Nil
g.toList :: others
}
def toProject(ss: List[String]): Project = {
var p = Project("", "", "")
for (line <- ss; parts = line split '=') parts match {
case Array("User", u) => p = p.copy(user = u)
case Array("Project", n) => p = p.copy(name = n)
case Array(k, _) => sys error s"Bad keyword $k"
case Array(text) => p = p.copy(description = s"${p.description} $text")
}
p
}
val ps = grafs(sample.lines) map toProject
Console println ps
}
Czy możesz podać przykład pliku test.txt i listę, którą chcesz pobrać? – maks
Podałem prawidłowy przykład jako przykład. :) –
A jaka powinna być zawartość listy wyników? – maks