如何扩展Scala列表以启用切片不是通过显式位置,而是通过给定的谓词/条件 [英] How to extend a Scala list to enable slicing not by explicit position but by given predicate/condition

查看:1380
本文介绍了如何扩展Scala列表以启用切片不是通过显式位置,而是通过给定的谓词/条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  trait Item 
case class TypeA(i:Int)extends Item
case class TypeB(i:Int)extends Item

考虑Scala项目列表,例如

  val myList = List(TypeA(1),TypeB(11),TypeB(12),
TypeA(2),TypeB 21),
TypeA(3),TypeB(31))

目标是定义一个可以应用到 myList 并且以谓词或条件作为参数的新的 slice 方法;例如

  myList.slice {x => x.isInstanceOf [TypeA]} 

会提供

  List(List(TypeA(1),TypeB(11),TypeB(12)),
List(TypeA(2),TypeB(21)),
List(TypeA(3),TypeB(31)))

相同的结果将通过

  myList.slice {case TypeA(x)=> x < 10} 

非常感谢。

解决方案

列表已经有一个 slice 方法 - 开始和结束索引。您要查找的是重复应用 span 方法:

  def span(p:(A)⇒Boolean):(List [A],List [A])

其中记录为:


根据谓词将此列表拆分为前缀/后缀对。



注意:c span p等价于(但可能比)更有效(c takeWhile p,c dropWhile p),只要谓词p的求值不会导致任何side-

返回一个由元素都满足p的列表中最长的前缀组成的对,其余的此列表。


您可以通过反复使用此方法获得所需的反谓谓词和额外的逻辑位,以确保没有返回的列表是空的。

  import annotation.tailrec 

def multiSpan [A] (xs:List [A])(splitOn:(A)=> Boolean):List [List [A]] = {
@tailrec
def loop :List [List [A]]):List [List [A]] = xs match {
case Nil => acc

case x :: Nil => List(x):: acc

case h :: t =>
val(pre,post)= t.span(!splitOn(_))
loop(post,(h :: pre):: acc)
} xs,Nil).reverse
}

UPDATE p>

根据对原始帖子的评论请求,这里有一个版本丰富列表,而不是一个独立的方法:

 隐含类AddMultispanToList [A](val list:List [A])extends AnyVal {
def multiSpan(splitOn:(A)=> Boolean):List [List [A ]] = {
@tailrec
def loop(xs:List [A],acc:List [List [A]]):List [List [A]] = xs match {
case Nil => acc

case x :: Nil => List(x):: acc

case h :: t =>
val(pre,post)= t.span(!splitOn(_))
loop(post,(h :: pre):: acc)
} list,Nil).reverse
}
}

p>

  myList.multiSpan(_。isInstanceOf [TypeA])


For

trait Item
case class TypeA(i: Int) extends Item
case class TypeB(i: Int) extends Item

consider a Scala list of items such as

val myList = List(TypeA(1), TypeB(11), TypeB(12), 
                  TypeA(2), TypeB(21), 
                  TypeA(3), TypeB(31))

The goal is to define a new slice method that can be applied onto myList and which takes a predicate or condition as argument; for instance

myList.slice { x => x.isInstanceOf[TypeA] }

would deliver

List(List(TypeA(1), TypeB(11), TypeB(12)), 
     List(TypeA(2), TypeB(21)), 
     List(TypeA(3), TypeB(31)))

In this example, an identical result would be achieved by

myList.slice { case TypeA(x) => x < 10 }

Many Thanks.

解决方案

List already has a slice method - it takes a subset of elements between a start and end index. What you're looking for is repeated application of the span method:

def span(p: (A) ⇒ Boolean): (List[A], List[A])

Which is documented as:

Splits this list into a prefix/suffix pair according to a predicate.

Note: c span p is equivalent to (but possibly more efficient than) (c takeWhile p, c dropWhile p), provided the evaluation of the predicate p does not cause any side-effects.

returns: a pair consisting of the longest prefix of this list whose elements all satisfy p, and the rest of this list.

You can get what you need by repeatedly using this method with an inverse predicate, and an extra bit of logic to ensure that none of the returned Lists are empty.

import annotation.tailrec

def multiSpan[A](xs: List[A])(splitOn: (A) => Boolean): List[List[A]] = {
  @tailrec
  def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
    case Nil => acc

    case x :: Nil => List(x) :: acc

    case h :: t =>
      val (pre,post) = t.span(!splitOn(_))
      loop(post, (h :: pre) :: acc)
  }
  loop(xs, Nil).reverse
}

UPDATE

As requested in comments on the original post, here's a version that enriches list instead of being a standalone method:

implicit class AddMultispanToList[A](val list: List[A]) extends AnyVal {
  def multiSpan(splitOn: (A) => Boolean): List[List[A]] = {
    @tailrec
    def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
      case Nil => acc

      case x :: Nil => List(x) :: acc

      case h :: t =>
        val (pre,post) = t.span(!splitOn(_))
        loop(post, (h :: pre) :: acc)
    }
    loop(list, Nil).reverse
  }
}

Use as:

myList.multiSpan(_.isInstanceOf[TypeA])

这篇关于如何扩展Scala列表以启用切片不是通过显式位置,而是通过给定的谓词/条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆