簡體   English   中英

Scala:如何將序列的子集映射到較短的序列

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM