Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 重写更实用的字符串修改_Scala - Fatal编程技术网

Scala 重写更实用的字符串修改

Scala 重写更实用的字符串修改,scala,Scala,我在读文件中的行 for (line <- Source.fromFile("test.txt").getLines) { .... } 我基本上想要一个列表[项目],其中Project看起来像 class Project (val User: String, val Name:String, val Desc: String) {} 描述是一大块文本,它不是以=开头,而是可以延伸到任意数量的行 我知道如何以迭代的方式完成这项工作。只需对关键字执行一系列检查,并填充一个类的实例,然

我在读文件中的行

for (line <- Source.fromFile("test.txt").getLines) {
  ....
}
我基本上想要一个列表[项目],其中
Project
看起来像

class Project (val User: String, val Name:String, val Desc: String) {}
描述是一大块文本,它不是以
=
开头,而是可以延伸到任意数量的行

我知道如何以迭代的方式完成这项工作。只需对关键字执行一系列检查,并填充一个类的实例,然后将其添加到列表中,以便稍后返回

但我认为应该可以用适当的函数风格来实现这一点,可能是使用
匹配大小写、yield
和递归,从而生成一个包含
User
Project
等字段的对象列表。使用的类和所有关键字都是已知的,并且文件格式也不是一成不变的。我主要是想学习更好的功能性风格

class Project (val User: String, val Name:String, val Desc: String) {}
object Project {
  def apply(str: String): Project = {
    val user = somehowFetchUserName(str)
    val name = somehowFetchProjectName(str)
    val desc = somehowFetchDescription(str)
    new Project(user, name, desc)
  }
}

val contents: Array[String] = Source.fromFile("test.txt").mkString.split("\\n\\n")
val list = contents map(Project(_))

将以项目列表结束。

您显然正在解析某些内容,因此可能是时候使用。。。解析器

由于您的语言似乎将换行符视为有意义的,因此您需要参考来告诉解析器

除此之外,一个相当简单的实现是

import scala.util.parsing.combinator.RegexParsers

case class Project(user: String, name: String, description: String)

object ProjectParser extends RegexParsers {
  override val whiteSpace = """[ \t]+""".r

  def eol : Parser[String] = """\r?\n""".r

  def user: Parser[String] = "User=" ~> """[^\n]*""".r <~ eol
  def name: Parser[String] = "Project=" ~> """[^\n]*""".r <~ eol
  def description: Parser[String] = repsep("""[^\n]+""".r, eol) ^^ { case l => l.mkString("\n") }
  def project: Parser[Project] = user ~ name ~ description ^^ { case a ~ b ~ c => Project(a, b, c) }
  def projects: Parser[List[Project]] = repsep(project,eol ~ eol)
}

另一种可能的实现(因为这个解析器相当简单),使用递归:

import scala.io.Source
case class Project(user: String, name: String, desc: String)
@scala.annotation.tailrec
def parse(source: Iterator[String], list: List[Project] = Nil): List[Project] = {
  val emptyProject = Project("", "", "")
  @scala.annotation.tailrec
  def parseProject(project: Option[Project] = None): Option[Project] = {
    if(source.hasNext) {
      val line = source.next
      if(!line.isEmpty) {
        val splitted = line.span(_ != '=')
        parseProject(splitted match {
          case (h, t) if h == "User" => project.orElse(Some(emptyProject)).map(_.copy(user = t.drop(1)))
          case (h, t) if h == "Project" => project.orElse(Some(emptyProject)).map(_.copy(name = t.drop(1)))
          case _ => project.orElse(Some(emptyProject)).map(project => project.copy(desc = (if(project.desc.isEmpty) "" else project.desc ++ "\n") ++ line))
        })
      } else project
    } else project
  }

  if(source.hasNext) {
    parse(source, parseProject().map(_ :: list).getOrElse(list))
  } else list.reverse
}
以及测试:

object Test {
  def source = Source.fromString("""User=Hans
Project=Blow up the moon
The slugs are going to eat the mustard. // multiline possible!
They are sneaky bastards, those slugs.

User=Plop
Project=SO
Some desc""")

  def test = println(parse(source.getLines))
}
其中:

List(Project(Hans,Blow up the moon,The slugs are going to eat the mustard. // multiline possible!
They are sneaky bastards, those slugs.), Project(Plop,SO,Some desc))

要回答您的问题而不进行关键词解析,请折叠行并聚合行,除非它是空的,在这种情况下,您将开始一个新的空段落

lines.foldLeft(List("")) { (l, x) => 
    if (x.isEmpty) "" :: l else (l.head + "\n" + x) :: l.tail  
} reverse
您会注意到它在处理零行、多行和尾随空行的方式上有一些折痕。适应你的需要。此外,如果您对字符串连接非常熟悉,那么可以将它们收集到嵌套列表中,并最终展平(使用.map(uu.mkString)),这只是为了展示将序列折叠为新序列而不是标量的基本技术


这会以相反的顺序生成列表,因为在每个步骤中,列表前置(::)比追加到l更有效。

显然您正在生成某些内容,因此您可能希望尝试。。。建筑工人

像尤尔根一样,我的第一个想法是折叠,你在那里积累结果

一个可变的.Builder以可变的方式进行累积,使用collection.generic.CanBuildFrom指示要用于从源集合生成目标集合的构建器。你让可变的东西保持足够长的时间以得到结果。这就是我对局部易变性的理解。以免假设从List[String]到List[Project]的路径是不可变的

对于其他的好答案(非负面评价),我想补充一点,功能风格意味着功能分解,通常是小功能

如果您没有使用正则表达式解析器,请不要忽略模式匹配中的正则表达式

尽量省去这些点。事实上,我相信明天是一个空闲的Dots日,对Dots敏感的人被建议呆在室内

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
}
火柴也可以这样捣碎:

  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)
  }
在折叠之前,先做段落似乎更简单。这是命令式思维吗

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
}
objecttest0使用示例扩展应用程序{
def-grafs(ss:Iterator[String]):List[List[String]={
val(g,rest)=ss dropWhile(u.isEmpty)span(u.nonEmpty)
val others=if(rest.nonEmpty)grafs(rest)else Nil
g、 托利斯特:其他
}
def-toProject(ss:List[String]):项目={
var p=项目(“,”,“)
对于(行p=p.copy(用户=u)
案例数组(“Project”,n)=>p=p.copy(name=n)
案例数组(k,)=>系统错误s“错误关键字$k”
大小写数组(text)=>p=p.copy(description=s“${p.description}$text”)
}
P
}
val ps=grafs(采样线)映射到项目
控制台打印
}

你能提供test.txt的示例和你想要的列表吗?我正确地将示例标记为示例:)以及结果列表的内容应该是什么?+1表示卑鄙的混蛋。他们可能行动迟缓,但到了早上他们已经造成了破坏。不完全如此,我添加了更精确的细节。我接受这一点是因为它很容易理解(不包括始终神秘的正则表达式),尽管提供的其他解决方案也相当简洁,我学到了很多。谢谢大家。
  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)
  }
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
}