[英]Scala: How to map a subset of a seq to a shorter seq
我正在尝试使用另一个(较短)序列映射一个序列的子集,同时保留不在该子集中的元素。 下面的玩具示例尝试只给雌性送花:
def giveFemalesFlowers(people: Seq[Person], flowers: Seq[Flower]): Seq[Person] = {
require(people.count(_.isFemale) == flowers.length)
magic(people, flowers)(_.isFemale)((p, f) => p.withFlower(f))
}
def magic(people: Seq[Person], flowers: Seq[Flower])(predicate: Person => Boolean)
(mapping: (Person, Flower) => Person): Seq[Person] = ???
有没有一种实现魔术的优雅方法?
在flowers
使用迭代器,每次predicate
成立时消耗一个; 代码看起来像这样,
val it = flowers.iterator
people.map ( p => if (predicate(p)) p.withFlowers(it.next) else p )
zip(aka zipWith)呢?
scala> val people = List("m","m","m","f","f","m","f")
people: List[String] = List(m, m, m, f, f, m, f)
scala> val flowers = List("f1","f2","f3")
flowers: List[String] = List(f1, f2, f3)
scala> def comb(xs:List[String],ys:List[String]):List[String] = (xs,ys) match {
| case (x :: xs, y :: ys) if x=="f" => (x+y) :: comb(xs,ys)
| case (x :: xs,ys) => x :: comb(xs,ys)
| case (Nil,Nil) => Nil
| }
scala> comb(people, flowers)
res1: List[String] = List(m, m, m, ff1, ff2, m, ff3)
如果顺序不重要,则可以获取以下简洁代码:
scala> val (men,women) = people.partition(_=="m")
men: List[String] = List(m, m, m, m)
women: List[String] = List(f, f, f)
scala> men ++ (women,flowers).zipped.map(_+_)
res2: List[String] = List(m, m, m, m, ff1, ff2, ff3)
我假设您想保留所有开始的人(而不是简单地过滤掉雌性并失去雄性),并且保留原始顺序。
嗯,有点丑陋,但是我想到的是:
def giveFemalesFlowers(people: Seq[Person], flowers: Seq[Flower]): Seq[Person] = {
require(people.count(_.isFemale) == flowers.length)
people.foldLeft((List[Person]() -> flowers)){ (acc, p) => p match {
case pp: Person if pp.isFemale => ( (pp.withFlower(acc._2.head) :: acc._1) -> acc._2.tail)
case pp: Person => ( (pp :: acc._1) -> acc._2)
} }._1.reverse
}
基本上是向左折,用一对由空的人列表和完整的花朵组成的对初始化“累加器”,然后在经过的人中循环。
如果当前人员是女性,则将当前鲜花列表的标题(“累加器”的字段2)传递给它,然后将已更新的累加器设置为已处理人员(正在增长)列表之前的已更新人员, (缩小)花朵列表的尾部。
如果是雄性,则只需添加到已处理人员列表中即可,而使花朵保持不变。
在首屏结束时,“累加器”(花朵)的第2栏应该是一个空列表,而第1栏则以相反的顺序容纳所有人(所有雌性都有各自的花朵),因此以._1.reverse
编辑 :尝试澄清代码(并替换一个类似于@elm的测试来替换match
)-希望可以更清楚地了解发生了什么,@ Felix! (没有,没有违法行为):
def giveFemalesFlowers(people: Seq[Person], flowers: Seq[Flower]): Seq[Person] = {
require(people.count(_.isFemale) == flowers.length)
val start: (List[Person], Seq[Flower]) = (List[Person](), flowers)
val result: (List[Person], Seq[Flower]) = people.foldLeft(start){ (acc, p) =>
val (pList, fList) = acc
if (p.isFemale) {
(p.withFlower(fList.head) :: pList, fList.tail)
} else {
(p :: pList, fList)
}
}
result._1.reverse
}
我显然缺少了一些东西,但不仅仅是
people map {
case p if p.isFemale => p.withFlower(f)
case p => p
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.