【问题标题】:Pattern matching in anonymous function in ScalaScala中匿名函数中的模式匹配
【发布时间】:2017-04-01 03:09:08
【问题描述】:

我是一名 Scala 初学者,在 Paul Chiusano 的《Scala 中的函数式编程》一书中的编码练习 (5.12) 中遇到问题。

我在这里有这个函数,叫做 unfold,它接受一个初始状态和一个用于生成具有下一个状态的流的函数:

def unfold[A,S](z: S)(f: S => Option[(A,S)]): Stream[A] = f(z) match {
  case Some((h,t)) => h #:: unfold(t)(f)
  case _ => Stream.empty
} 

例如,使用此功能可以创建无限的对象流,例如

def constant[A](a: A): Stream[A] = unfold(a)(_ => Some(a,a))

现在,我想创建斐波那契数列,然后输入:

def fibs: Stream[Int] = unfold((0,1))((a,b) => Some(a,(b,a+b)))

我收到以下错误:

  • 缺少参数类型 b

  • Some[((Int,Int),(Nothing,String))] 类型的表达式不符合预期的类型 Option[(A_,(Int,Int))]

    李>

如果我在传递给 unfold 的匿名函数中使用 case 关键字,例如:

{ case (a,b) => Some(a,(b,a+b))}

一切都很好。

所以我的问题是:这两种实现有什么区别?是不是我不明白的类型推断?

【问题讨论】:

标签: scala functional-programming pattern-matching anonymous-function type-inference


【解决方案1】:
 { (a,b) => Some(a, (b, a+b)) }

是一个接受 两个 参数的函数 - ab,并返回一个(元组的)选项。这不是你需要的。您想定义一个接受单个(元组)参数的函数。一种方法是这样的:

 { tuple => Some(tuple._1, (tuple._2, tuple._1 + tuple._2)) }

你可以扩展成这样的东西:

 { tuple => 
     val a = tuple._1
     val b = tuple._2
     // Can also do it shorter, with automatic unapply here:
     // val (a,b) = tuple
     // this is already pretty similar to what you have with `case`, right?
     Some(a, (b, a+b))
 }

这看起来很长,但是 scala 有一个特殊的语法结构,可以让您“即时”将复杂参数解构为匿名函数,使用类似于模式匹配的语法(它实际上是在输入时调用unapply,与模式相同匹配会做):

 { case(a, b) => Some(a, (b, a+b)) }

这和上面的完全一样。

【讨论】:

  • 编译器在这里没有调用unapply,它知道确切的类型,match 的工作方式相同。看起来unapply 仅适用于提取器对象。
  • @VictorMoroz 你是对的,元组是特殊处理的。当我提到 unapply 时,我想到了一个通用案例,您可以在这种情况下使用提取器对象。
【解决方案2】:

您似乎已经达到了关于编译器如何解释您的代码的规则之一:

A normal function of one arg:

scala> val g = (x: Int) => x + 1
g: Int => Int = <function1>

The data type for a tuple of ints is Tuple2(Int, Int):
scala> (3,4)
res3: (Int, Int) = (3,4)

scala> val a: Tuple2[Int, Int] = (3,4)
a: (Int, Int) = (3,4)

But this does not work:

scala> val f = ((a,b): (Int, Int)) => a+b
<console>:1: error: not a legal formal parameter.
Note: Tuples cannot be directly destructured in method or function parameters.
      Either create a single parameter accepting the Tuple1,
      or consider a pattern matching anonymous function: `{ case (param1, param1) => ... }
val f = ((a,b): (Int, Int)) => a+b
              ^
This works:
scala> val f = (x: Tuple2[Int, Int]) => x._1 + x._2
f: ((Int, Int)) => Int = <function1>

这是你认为你可以做到的:

val f = ((a, b): (Int, Int)) => Some(a, (b, a + b))

然后(在unfold 的上下文中)(0,1) 被理解为类型(Int, Int),因此当您调用unfold 时,您可以省略类型声明而只写((a, b)) =&gt; Some(a, (b, a + b))。但它不起作用,因为“元组不能直接在方法或函数参数中解构”。 上面的函数f 在你的unfold 之外甚至不能单独编译。相反,它编译: val g = (x: Tuple2[Int, Int]) =&gt; Some(x._1, (x._2, x._1 + x._2))

【讨论】:

    猜你喜欢
    • 2022-07-12
    • 1970-01-01
    • 2023-03-21
    • 2014-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-31
    • 1970-01-01
    相关资源
    最近更新 更多