Scala:如何将seq的子集映射到较短的seq

时间:2015-09-08 06:28:52

标签: scala

我试图使用另一个(较短的)序列映射序列的子集,同时保留不在子集中的元素。下面的玩具示例试图仅向女性献花:

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] = ??? 

有没有一种优雅的方式来实现魔法?

4 个答案:

答案 0 :(得分:2)

每次flowers成立时,使用predicate上的迭代器,消耗一个;代码看起来像这样,

val it = flowers.iterator
people.map ( p => if (predicate(p)) p.withFlowers(it.next) else p )

答案 1 :(得分:0)

zip(又名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)

答案 2 :(得分:0)

我打算假设你想要保留所有的起始人(不仅仅是过滤掉女性并失去男性),也按照原来的顺序。

嗯,有点难看,但我想出的是:

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.reverse

结束

编辑:尝试澄清代码(并替换类似于@ elm' s的测试以替换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
}

答案 3 :(得分:-1)

我显然错过了一些东西,但不仅仅是

people map {
  case p if p.isFemale => p.withFlower(f)
  case p => p
}
相关问题