带下划线的scala传递函数产生的函数不是值

时间:2015-01-23 20:23:56

标签: scala functional-programming

您好我正在编写将函数传递给map的任何可能的变体,我最初的理解是它们都会产生相同的结果,但我发现第2,3行实际上产生了不同的输出,第4行是个谜给我

def g(v: Int) = List(v - 1, v, v + 1)
    val l = List(1, 2, 3, 4, 5)
    // map with some variations
    println(l.map { x => g(x) })
    println(l.map { (_: Int) => g(_) }) // line 2
    println(l.map { (_) => g(_) }) // line 3
    println(l.map { _ => }) // line 4
    println(l.map { g(_) })
    println(l.map { g })

输出:

List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))
List(<function1>, <function1>, <function1>, <function1>, <function1>)
List(<function1>, <function1>, <function1>, <function1>, <function1>)
List((), (), (), (), ())
List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))
List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))

2 个答案:

答案 0 :(得分:6)

这些都是将函数g的应用程序传递给List的每个元素的等效方法:

l.map { x => g(x) }
l.map { g(_) }
l.map { g }

res17: List[List[Int]] = List(List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 6))

这些是将List的所有元素映射到g等未应用函数的等效方法:

l.map { (_: Int) => g(_) }
l.map { (_) => g(_) }

也就是说,映射列表的每个元素实际上都是g

scala> l.map { (_: Int) => g(_) }.head
res23: Int => List[Int] = <function1>
scala> res23(0)
res24: List[Int] = List(-1, 0, 1)

实际上,这两者之间的唯一区别是括号和类型注释。它们都相当于:

l.map { _ => g(_) }

以下内容仅将List的所有元素映射到Unit

l.map { _ => }

答案 1 :(得分:3)

让我们从那些返回你期望的结果开始:

println(l.map { x => g(x) })
println(l.map { g(_) })
println(l.map { g })

可以推断这三者都是一回事。实际上,第三个取决于map 期望一个函数的事实,但实际上,它们实际上基本相同。

从最后一个开始,g是一个命名函数。您使用名称g对其进行了定义,并使用Int并返回List[Int]

接下来,第一个:x => g(x)。这是一个匿名函数。它没有名称,它的定义就在那里,在那里写入:它采用参数x(推断为Int),并返回List[Int] (因为这就是调用g(x))的作用。

现在,gx => g(x)不是一回事。它们具有相同类型Int => List[Int],但它们是不同的函数,就像我定义def h(x: Int) = g(x)hg一样相同的类型,但功能不同。

离开g(_)。这是x => g(x)的语法糖。在这种情况下,g(_)x => g(x)实际上是相同的。这是类似_ + _的特殊情况,这是一个表达式,其中下划线表示函数的参数。有人会认为g(_)等于g(x => x),但这种扩展为x => x的情况是将函数“移动”到下一个外表达边界的异常。

所以,让我们看看让你怀疑的案例:

println(l.map { (_: Int) => g(_) }) // line 2

首先,我们知道g(_)x => g(x)相同,所以此行等同于

println(l.map { (_: Int) => (x => g(x)) }) // line 2

剩下的下划线与g(_)中的下划线完全无关。替代参数名称的下划线表示参数名称无关。这与撰写本文基本相同:

println(l.map { (unusedVar: Int) => (x => g(x)) }) // line 2

至于类型,它是Int => (Int => List[Int])。因此,当您映射列表时,您会得到Int => List[Int] - 函数的列表! - 打印的是什么。

println(l.map { (_) => g(_) }) // line 3

与第2行相同,只是省略了参数的类型,无论如何都要推断出来。

最后,

println(l.map { _ => }) // line 4

该类型为Int => Unit。它只需要一个参数,该参数被忽略,不执行任何操作,并返回Unit(类似于Java中的void类型)。

Unit是一种价值无关紧要的东西。函数来返回一些东西,因为这就是函数所做的事情。当这个问题无关紧要时,我们使用Unit类型,它只有一个值。 Unit的唯一值写为()

例如,这会返回true

def test = {
  val a = println("Println returns Unit")
  val b: Unit = () // the only possible value
  a == b
}

这就是为什么你看到为第4行打印()的列表:它是List[Unit],因此,它的所有元素都是()