[英]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.