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

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

问题描述

为了

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

考虑一个 Scala 项目列表,例如

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))

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

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] }

将交付

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 }

非常感谢.

推荐答案

List 已经有一个 slice 方法 - 它需要开始和结束索引之间的元素子集.您正在寻找的是重复应用 span 方法:

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])

记录为:

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

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

注意:c span p 等价于(但可能比)(c takeWhile p, c dropWhile p),前提是谓词 p 的计算不会引起任何副作用.

返回:由这个列表的最长前缀组成的一对,其元素都满足p,以及这个列表的其余部分.

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

通过使用逆谓词和额外的逻辑来确保返回的 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
}

更新

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

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
  }
}

用作:

myList.multiSpan(_.isInstanceOf[TypeA])

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

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