Mik*_*ike 5 scala pattern-matching
我在原帖中解释我正在寻找的东西显然做得很差,所以让我们再试一次.我想要完成的是能够传递一系列项目,提取一个或多个项目,然后将序列的REMAINDER传递给另一个提取器.注意,按顺序,我的意思是序列(不一定是List).我之前的例子使用list作为序列,我给出了一些使用cons(::)提取的例子,但我也可以将一个数组作为我的序列传递.
我以为我知道模式匹配和提取是如何工作的但我可能是错的,所以为了避免任何更基本的评论和链接到如何进行模式匹配网站这里是我的理解:
如果我想从提取器返回单个项目,我将定义一个unapply方法.此方法采用我选择的任何类型作为输入(类型可以是序列...)并返回单个可选项(返回类型本身可以是序列).如果我想要匹配,则返回必须包含在Some中.如果不需要,则返回None.下面是一个示例,它将序列作为输入并返回包含在Some中的相同序列,但前提是它包含所有字符串.我很可能只是返回包含在Some中的序列而不做其他任何事情,但这似乎会给人们造成混乱.关键是如果它被包装在Some中那么它将匹配,如果它是None,它将不会.为了更清楚,除非输入也匹配我的unapply方法输入类型,否则匹配也不会发生.这是我的例子:
object Test {
// In my original post I just returned the Seq itself just to verify I
// had matched but many people commented they didn't understand what I
// was trying to do so I've made it a bit more complicated (e.g. match
// only if the sequence is a sequence of Strings). Hopefully I don't
// screw this up and introduce a bug :)
def unapply[A](xs: Seq[A]): Option[Seq[String]] =
if (xs forall { _.isInstanceOf[String] })
Some(xs.asInstanceOf[Seq[String]])
else
None
}
Run Code Online (Sandbox Code Playgroud)
以List为例,我现在可以执行以下操作:
// This works
def test1(xs: List[_]) = xs match {
case (s: String) :: Test(rest) =>
println("s = " + s + ", rest = " + rest)
case _ =>
println("no match")
}
test1(List("foo", "bar", "baz")) // "s = foo, rest = List(bar, baz)"
Run Code Online (Sandbox Code Playgroud)
我的test1函数将List作为输入,并通过构造函数模式使用cons提取头部和尾部(例如::( s,rest)).然后使用类型ascription(:String)来确保head(s)是一个String.尾部包含List("bar","baz").这是一个List,这意味着它也是一个Seq(序列).然后它作为输入传递给我的Test提取器,它验证"bar"和"baz"都是字符串并返回包含在Some中的List.由于Some被返回,它被认为是匹配(虽然在我的原始帖子中,我无意中将unapplySeq与unapply混合,但这并没有按预期工作,但除此之外......).这不是我想要的.这只是一个示例,表明Test确实提取了Seq作为预期的输入.
现在,这是我上次无意中使用unapplySeq而不是在我的写作中不应用时造成大量混淆的地方.在试图理解发布的评论之后我感到非常困惑,我终于找到了错误.非常感谢Dan指出我正确的方向......
但是,为了避免混淆,让我澄清一下我对unapplySeq的理解.像unapply一样,unapplySeq接受我选择作为输入的任何参数,但它不返回单个元素,而是返回一系列元素.然后,此序列中的每个项目都可用于其他模式匹配.同样,为了使匹配发生,输入类型必须匹配,并且我返回的序列必须包含在Some中,而不是None.当从unapplySeq返回的项目序列中提取时,您可以使用_*匹配尚未匹配的任何剩余项目.
好的,所以我的提取器将一个序列作为输入并返回一个序列(作为单个项目)作为回报.由于我只想返回一个项目作为匹配,我需要使用unapply not unapplySeq.即使在我的情况下,我正在返回一个Seq,我不想要unapplySeq,因为我不想对Seq中的项目进行更多的模式匹配.我只想将这些项目作为Seq返回,然后传递给我的案例匹配的主体.这听起来令人困惑,但对那些理解unapply vs unapplySeq的人我希望不是.
所以这就是我想要做的.我想采取一些返回序列的东西(例如List或Array),我想从这个序列中提取一些项目,然后提取项目的REMAINDER(例如_*)作为序列.我们称之为余数序列.我想将剩余序列作为输入传递给我的提取器.如果符合我的标准,我的提取器将把剩余的项目作为单个Seq返回.只是要100%清楚.List(或Array等)将调用其unapplySeq提取器来创建项目序列.我将提取这些项中的一个或多个,然后将剩下的作为序列传递给我的Test提取器,它将使用unapply(NOT unapplySeq)返回余数.如果您对此感到困惑,请不要发表评论......
这是我的测试:
// Doesn't compile. Is there a syntax for this?
def test2(xs: Seq[_]) = xs match {
// Variations tried:
// Test(rest) @ _* - doesn't compile (this one seems reasonable to me)
// Test(rest @ _*) - doesn't compile (would compile if Test had
// unapplySeq, but in that case would bind List's
// second element to Test as a Seq and then bind
// rest to that Seq (if all strings) - not what I'm
// looking for...). I though that this might work
// since Scala knows Test has no unapplySeq only
// unapply so @ _* can be tied to the List not Test
// rest @ Test(_*) - doesn't compile (didn't expect to)
case List(s: String, Test(rest) @ _*) =>
println("s = " + s + " rest = " + rest)
case _ =>
println("no match")
}
// This works, but messy
def test3(xs: List[_]) = xs match {
case List(s: String, rest @ _*) if (
rest match { case Test(rest) => true; case _ => false }
) =>
println("s = " + s + " rest = " + rest)
case _ =>
println("no match")
}
Run Code Online (Sandbox Code Playgroud)
我根据Julian的评论创建了test3(感谢Julian ..).有些人评论说test3做了我想要的,所以他们对我正在寻找的东西感到困惑.是的,它完成了我想要完成的任务,但我对此并不满意.丹尼尔的例子也有效(感谢丹尼尔),但我也不满意必须创建另一个提取器来分割事物然后进行嵌入式提取.这些解决方案似乎太多了,以便完成对我来说相当直接的事情.我想要的是使test2工作或者知道它不能以这种方式完成.是否因为语法错误而给出了错误?我知道休息@ _*会返回一个Seq,可以在这里验证:
def test4(xs: List[_]) = xs match {
case List(s: String, rest @ _*) =>
println(rest.getClass) // scala.collection.immutable.$colon$colon
case _ =>
println("no match")
}
Run Code Online (Sandbox Code Playgroud)
它返回cons(::),这是一个Seq的List.那么如何将_*Seq传递给我的提取器并将其返回绑定到变量rest?
请注意,我也尝试将varargs传递给我的unapply构造函数(例如unapply(xs:A*)...)但是也不匹配.
所以,我希望现在很清楚,当我说我想在模式匹配中提取序列的其余部分时.我不知道我怎么能说出来.
根据Daniel的反馈,我希望他能给我一个答案:)
我想提取第一个项目并将剩余部分传递给另一个提取器.
好.你test1完全是这样做的. first_item :: Extractor(the_rest).您看到的奇怪行为来自您的Test提取器.因为你已经得到了你所陈述的问题的答案,并且你的预期行为是你Test的问题test1,看起来你真正想要的是对提取器的一些帮助.
因此,请阅读docs.scala-lang.org中的Extractor Objects和Scala中的Pattern Matching(pdf).虽然该PDF有一个示例unapplySeq,并建议您想要使用它,但这里有一些额外的例子:
object Sorted {
def unapply(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
object SortedSeq {
def unapplySeq(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
Run Code Online (Sandbox Code Playgroud)
交互方式:
scala> List(1,2,3,4) match { case Sorted(xs) => Some(xs); case _ => None }
res0: Option[Seq[Int]] = Some(List(1, 2, 3, 4))
scala> List(4,1,2,3) match { case Sorted(xs) => Some(xs); case _ => None }
res1: Option[Seq[Int]] = None
scala> List(4,1,2,3) match { case first :: Sorted(rest) => Some(first, rest); case _ => None }
res2: Option[(Int, Seq[Int])] = Some((4,List(1, 2, 3)))
scala> List(1,2,3,4) match { case SortedSeq(a,b,c,d) => (a,b,c,d) }
res3: (Int, Int, Int, Int) = (1,2,3,4)
scala> List(4,1,2,3) match { case _ :: SortedSeq(a, b, _*) => (a,b) }
res4: (Int, Int) = (1,2)
scala> List(1,2,3,4) match { case SortedSeq(a, rest @ _*) => (a, rest) }
res5: (Int, Seq[Int]) = (1,List(2, 3, 4))
Run Code Online (Sandbox Code Playgroud)
或许 - 我对此只有微弱的怀疑,你没有说太多 - 你不想要提取器帮助,但实际上你想要一种简洁的方式表达类似的东西
scala> List(1,2,3,4) match { case 1 :: xs if (xs match { case Sorted(_) => true; case _ => false }) => xs }
res6: List[Int] = List(2, 3, 4)
Run Code Online (Sandbox Code Playgroud)
Erlang有这样的功能(虽然没有这些疯狂的提取器):
example(L=[1|_]) -> examine(L).
Run Code Online (Sandbox Code Playgroud)
,哪个模式匹配相同的参数两次 - L也适用于[1|_].在Erlang中,两侧=都是完整的模式,可以是任何东西,你可以添加更多的第三个或更多模式=.Scala似乎只支持L=[1|_]表单,有一个变量,然后是一个完整的模式.
scala> List(4,1,2,3) match { case xs @ _ :: Sorted(_) => xs }
collection.immutable.::[Int] = List(4, 1, 2, 3)
Run Code Online (Sandbox Code Playgroud)
::是一个提取器。有关其工作原理(通过随机谷歌搜索),请参阅此处。
def test1(xs: List[_]) = xs match {
case s :: rest =>
println("s = " + s + " rest = " + rest)
case _ =>
println("no match")
}
scala> test1(List("a", "b", "c"))
s = a rest = List(b, c)
Run Code Online (Sandbox Code Playgroud)
我想这就是你想要的?