这有组合器吗?

时间:2015-04-08 13:11:48

标签: scala recursion combinators

我有一个字符串列表

val allLines = List("James Bond", "secret agent", "", "Martin Odersky")

然后我想" map"对... List[Person]其中case class Person(name: String, moreDetails: List[String]一次使用几个元素

val people = allLines.someCombinatorFunction { lines => 
   val name = lines.head
   val (moreDetails, remainingLines) = lines.span(_ != "")
   val person = Person(name, moreDetails)

   (person, remainingLines)
}

这应该给我:

List(Person("James Bond", List("secret agent")), Person("Martin Odersky", Nil))

IE中。我想采用一个可变数字行,将它们组合成一个人,并且"交出"剩下的台词。 List[String] => List[Person]。这对于递归来说是微不足道的:

def linesToPeople(lines: List[String]): List[Person] = { lines => 
   val name = lines.head
   val (moreDetails, remainingLines) = lines.span(_ != "")
   val person = Person(name, moreDetails)

   person :: linesToPeople(remainingLines)
}

...但是!,递归是昂贵的,除非你使它尾递归..:

def linesToPeople(lines: List[String], acc: List[Person] = Nil): List[Person] = { lines => 
   val name = lines.head
   val (moreDetails, remainingLines) = lines.span(_ != "")
   val person = Person(name, moreDetails)

   linesToPeople(remainingLines, person :: acc)
}

^这就是它变得有点过于繁琐的地方。您还需要在最后执行.reverse以获得正确的订单。组合器在这里会很好

所以基本上,我有一个列表,我想"消费" &合并其可变数量的元素,并返回遗骸。有没有办法在不诉诸递归的情况下做到这一点?

2 个答案:

答案 0 :(得分:4)

Scalaz有一个函数selectSplit

import scalaz._
import Scalaz._

def getPeople(lines: List[String]): List[Person] = 
  lines.selectSplit(_ != "").map(l => Person(l.head, l.tail))

然后:

scala> getPeople(List(
  "James Bond", "secret agent", "", 
  "Martin Odersky", "", 
  "Arnold Schwarzenegger", "Terminator", "governor"))
res8: List[Person] = List(Person(James Bond,List(secret agent)), Person(Martin Odersky,List()), Person(Arnold Schwarzenegger,List(Terminator, governor)))

答案 1 :(得分:1)

你可以用折叠来做到这一点 - 它可能不像你想要的那样特别适用,并且它有点吵,但它有效:

val (last, rest) =
  allLines.foldLeft((None: Option[Person], List.empty[Person])) {
    case ((None, people), line) => (Some(Person(line, Nil)), people)
    case ((Some(last), people), "") => (None, people :+ last)
    case ((Some(Person(name, details)), people), line) =>
      (Some(Person(name, details :+ line)), people)
  }

val people = rest ++ last

基本思想是你携带一个也表示处理状态的累加器。在这种情况下,我使用已完成人员列表和Option[Person]包含当前已添加详细信息的人员(如果有)。

我通常建议使用像折叠这样的组合器并避免显式递归,但在这种情况下,我认为它不是一个扣篮 - (显式)递归版本更清晰。