这有组合器吗?
Is there a combinator for this?
我有一个字符串列表
val allLines = List("James Bond", "secret agent", "", "Martin Odersky")
然后我想要 "map" 例如a 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))
即。我想采用可变数量的行,将它们组合成一个人,然后 "hand off" 剩余的行。 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)
}
...但是!,递归是昂贵的,除非你使它尾递归..:[=19=]
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
以获得正确的顺序。组合器在这里会很好
基本上,我有一个列表,我想 "consume" 并组合可变数量的元素,return 剩下的。有没有办法不用递归就可以做到这一点?
你可以通过折叠来做到这一点——它可能不像你想要的那样特别适用,而且有点吵,但它有效:
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]
。
我通常建议使用像 fold 这样的组合器并避免显式递归,但在这种情况下,我认为它不是灌篮——(显式)递归版本更清晰。
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)))
我有一个字符串列表
val allLines = List("James Bond", "secret agent", "", "Martin Odersky")
然后我想要 "map" 例如a 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))
即。我想采用可变数量的行,将它们组合成一个人,然后 "hand off" 剩余的行。 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)
}
...但是!,递归是昂贵的,除非你使它尾递归..:[=19=]
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
以获得正确的顺序。组合器在这里会很好
基本上,我有一个列表,我想 "consume" 并组合可变数量的元素,return 剩下的。有没有办法不用递归就可以做到这一点?
你可以通过折叠来做到这一点——它可能不像你想要的那样特别适用,而且有点吵,但它有效:
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]
。
我通常建议使用像 fold 这样的组合器并避免显式递归,但在这种情况下,我认为它不是灌篮——(显式)递归版本更清晰。
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)))