繁体   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