【问题标题】:Why can't I match on a Stream?为什么我不能在 Stream 上匹配?
【发布时间】:2017-03-02 01:53:59
【问题描述】:

具体来说:

scala> def f(n: Seq[Any]) = n match { 
     case Nil => "Empty" 
     case h :: t => "Non-empty" 
}
f: (n: Seq[Any])String

scala> f(Stream())
res1: String = Empty

scala> f(List(1))
res17: String = Non-empty

scala> f(Stream(1))
scala.MatchError: Stream(1, ?) (of class scala.collection.immutable.Stream$Cons)
  at .f(<console>:13)
  ... 33 elided

还有很多其他方法可以实现这一点,但在编写代码时是静态安全的,并且在运行时会失败。怎么回事?

【问题讨论】:

    标签: scala


    【解决方案1】:

    对于Streamconcat 符号应为#::模式匹配应为:

      def f(n: Seq[Any]) = n match {
        case Nil => "Empty"
        case h :: t => "Non-empty"
        case h #:: t => "Non-empty stream"
      }
    

    对于 :: 是对于 List / Seq 类型(List 扩展自 Seq:)),请参阅:

    final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
    

    【讨论】:

    • List 扩展自 Seq
    • @Nyavro Seq(1, 2)List。也就是说,List 是默认的Seq
    【解决方案2】:

    您只能使用:: 来解构List 而不是Stream。由于您提供的StreamList 不匹配,因此您将获得MatchError。如果您希望f 支持使用该类型匹配(提取器)的流,您可以使用#::

    def f(n: Seq[Any]) = n match {
      case Nil => "Empty"
      case h :: t => "Non-empty"
      case h #:: t => "Non-empty"
    }
    

    一般来说,这种方法非常脆弱,因为上面显示的两种提取器类型都只适用于这两种类型的Seq。其他人可能会打破。如果您只关心确定Seq 是否为空,那么只需使用n.nonEmptyn.isEmpty 并处理Boolean 结果。否则,试图为未密封的特征提供详尽匹配肯定会失败。

    您也可以使用Seq 提取器:

    def f(n: Seq[Any]) = n match { 
      case Nil => "Empty" 
      case Seq(_*) => "Non-empty" 
    }
    

    【讨论】:

    【解决方案3】:

    虽然其他答案正确显示了特定于Stream 的提取器,即#::,但存在Seq 的提取器(以及从SeqLike 派生的任何内容) - 它是+:,这适用于两者List(如::)和Stream(如`#::)。使用这个提取器,您可以轻松编写一个适用于两者的函数:

    def f(n: Seq[Any]) = n match { 
      case h +: t => "Non-empty" 
      case _ => "Empty" 
    }
    

    另见Scala pattern matching on sequences other than Lists

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多