标量跨度方法的行为不符合预期

时间:2018-06-23 20:57:28

标签: scala list

我有这个:

List('a','a', 'a', 'b')

我想得到这个:

List(List('a','a', 'a'), List('b'))

这是我的代码:

def pack[T](xs: List[T]): List[List[T]] = xs match {
  case Nil => Nil
  case x :: xs1 =>  val y = xs1.head
    println("x is " + x)
    println("y is " + y)
    val (head,tail) = xs1 span ( x => x.equals(xs1.head))
    println("head is " + head)
    println("tail is " + tail)
   (x :: head) ::  pack(tail)
}

pack(List('a','a', 'a', 'b'))

返回以下内容:

x is a
y is a
head is List(a, a)
tail is List(b)

java.util.NoSuchElementException: head of empty list

问题:

  1. 为什么我的头不是List(a,a,a)而尾巴不只是List(b)
  2. 为什么它为空列表而不是Nil返回错误?

谢谢

2 个答案:

答案 0 :(得分:4)

直接回答您的问题:

  1. case x :: xs1语句将x设置在xs的头部,并将xs1设置在其尾部。我想您可能希望您的span语句为xs1 span(_ == x):从xs1的尾部元素中选取与x的第一个元素xs相匹配的元素。因为您将span应用于xs1而不是xs,所以错过了第一个元素。 (不过,您确实要在其后加上该值的开头。)但是,您的span谓词看起来是xs1的开头,而不是x,从而导致错误-请参见答案( 2)。 (顺便说一句,您的x函数中有两个不同的pack变量,这使事情变得更加混乱,这就是为什么我将span谓词参数匿名化了。)
  2. 实际上它确实返回Nil以获得空列表。当xs仅包含一个元素时,即会出现错误,表示其尾部xs1为空。查看空列表的head会显示该错误。

如果您想要连续显示相同元素的单独列表,那么span将为您做到这一点:它将通过谓词的元素(与第一个字符相同的字符)放入其第一个返回列表中;但是一旦该谓词对于某个元素失败,该元素及其后的所有内容都会放入第二个列表中。因此,如果您期望如此,那是对的...

例如(在 Scala REPL 会话中):

$ scala
Welcome to Scala 2.12.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.

scala> val xs = List('a', 'a', 'a', 'b')
xs: List[Char] = List(a, a, a, b)

scala> xs.span(_ == 'a')
res0: (List[Char], List[Char]) = (List(a, a, a),List(b))

我将像这样重写您的pack函数,该函数更简单并避免了原始版本中的某些陷阱(即,删除xs列表的开头,然后使用{{1 }}尾巴上的目标字符有误,然后尝试通过重新前缀原始头来重建列表):

span

但是,如果元素混合在一起,那么您可能不喜欢结果:

scala> def pack[T](xs: List[T]): List[List[T]] = xs match {
     !
     !   // If xs is empty, return Nil.
     !   case Nil => Nil
     !
     !   // Otherwise, the list is non-empty, so just process it.
     !   case _ => {
     !     val (head, tail) = xs.span(_ == xs.head)
     !
     !     // Prefix head to packed tail.
     !     head :: pack(tail)
     !   }
     ! }
pack: [T](xs: List[T])List[List[T]]

scala> pack(xs)
res1: List[List[Char]] = List(List(a, a, a), List(b))

那是因为scala> val mixed = List('a', 'a', 'b', 'c', 'a', 'a', 'b', 'a') mixed: List[Char] = List(a, a, b, c, a, a, b, a) scala> pack(mixed) res2: List[List[Char]] = List(List(a, a), List(b), List(c), List(a, a), List(b), List(a)) 在谓词失败时停止查看元素。如果要将span'a''b'元素分成单独的列表,则需要'c'而不是partition:所有通过谓词的内容进入第一名单;一切都失败了您的span函数将变为:

pack

答案 1 :(得分:1)

x :: xs是用于匹配列表的开头(第一个元素)和结尾(其余)的模式。如果列表中包含1个元素,则tail将为Nil,但是您在该tail上调用.head会导致错误。我认为做您想要的事情的一种方法是:

  def pack[T](xs: List[T]): List[List[T]] = xs match {
      case Nil => Nil
      case x :: xs1 =>
          val (prefix, rest) = xs1 span (_.equals(x))
          (x :: prefix) :: pack(rest)
  }

对于许多不同的元素,虽然这可能不是最好的方法。