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

scala跨度方法的行为不符合预期,scala,list,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)

我有这个:

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
问题:

  • 为什么我的头不是列表(a,a,a),而尾巴不仅仅是列表(b)
  • 为什么它返回空列表的错误而不是Nil

  • 谢谢

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

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

    要直接回答您的问题:

  • case x::xs1
    语句将
    x
    设置为
    xs
    的头部,将
    xs1
    设置为其尾部。我想您可能希望您的
    span
    语句是
    xs1 span(==x)
    :从
    xs1
    中获取与
    xs
    的第一个元素
    x
    相匹配的元素。由于您将
    span
    应用于
    xs1
    ,而不是
    xs
    ,因此缺少了第一个元素。(不过,您会在头后面加上该值作为前缀。)但是,
    span
    谓词会查看
    xs1
    的头,而不是
    x
    ,从而导致错误-请参见答案(2)。(顺便说一句,您的
    pack
    函数中有两个不同的
    x
    变量,这使问题变得更加混乱,这就是我将
    span
    谓词参数匿名化的原因。)
  • 对于空列表,它实际上返回
    Nil
    。当
    xs
    只有一个元素时会出现错误,这意味着它的尾部
    xs1
    为空。查看空列表的
    标题
    ,会出现该错误
  • 如果您想要连续出现的相同元素的单独列表,那么
    span
    将为您做到这一点:它将传递谓词的元素(与第一个字符相同的字符)放入其第一个返回的列表中;但是,一旦某个元素的谓词失败,该元素和随后的所有内容都将被放入第二个列表中。所以如果你期待的话,你是对的

    例如(在Scala REPL会话中):

    $scala
    欢迎使用Scala 2.12.6(Java热点(TM)64位服务器虚拟机,Java 1.8.0_171)。
    键入要计算的表达式。或者尝试:帮助。
    scala>val xs=List('a','a','a','b')
    xs:List[Char]=列表(a,a,a,b)
    scala>xs.span(=='a')
    res0:(列表[Char],列表[Char])=(列表(a,a,a),列表(b))
    
    我会像这样重写你的
    pack
    函数,它更简单,并且避免了原始版本中的一些陷阱(即,删除
    xs
    列表的头部,然后在尾部使用
    span
    和错误的目标字符,然后尝试通过在原始头部添加前缀来重建列表):

    scala>def-pack[T](xs:List[T]):List[List[T]=xs-match{
    !
    !//如果xs为空,则返回Nil。
    !case Nil=>Nil
    !
    !//否则,列表为非空,请处理它。
    !case=>{
    !val(head,tail)=xs.span(==xs.head)
    !
    !//从头部到尾部的前缀。
    !头::包(尾)
    !   }
    ! }
    包装:[T](xs:List[T])列表[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')
    混合:列表[字符]=列表(a,a,b,c,a,a,b,a)
    scala>包装(混合)
    res2:List[List[Char]=List(List(a,a),List(b),List(c),List(a,a),List(b),List(a))
    
    这是因为当谓词失败时,
    span
    停止查看元素。如果您想将
    'a'
    'b'
    'c'
    元素分离到单独的列表中,那么您需要
    分区
    ,而不是
    span
    :传递谓词的所有内容都进入第一个列表;所有失败的事情都会进入第二阶段。您的
    pack
    功能将变为:

    scala>def pack2[T](xs:List[T]):List[List[T]=xs匹配{
    !
    !//如果xs为空,则返回Nil。
    !case Nil=>Nil
    !
    !//否则,列表为非空,请处理它。
    !case=>{
    !val(head,tail)=xs.partition(==xs.head)
    !
    !//从头部到尾部的前缀。
    !head::pack2(tail)
    !   }
    ! }
    pack2[T](xs:List[T])List[List[T]]
    scala>pack2(xs)
    res3:List[List[Char]=List(List(a,a,a),List(b))
    scala>pack2(混合)
    res4:List[List[Char]=List(List(List(a,a,a,a,a),List(b,b),List(c))