带有下划线的scala传递函数产生一个函数而不是一个值

scala passing function with underscore produces a function not a value

您好,我正在编写将函数传递给映射的任何可能变体,我最初的理解是它们都会产生相同的结果,但我发现第 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))

这些都是将函数 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 { _ => }

让我们从那些return您期望的结果开始:

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

可以推断这三者是一回事。实际上,第三个取决于map 期望一个功能,但说实话,它们确实基本相同。

从最后一个开始,g 是一个命名函数。您使用名称 g 定义了它,采用 Int 和 returning List[Int].

接下来,第一个:x => g(x)。这是一个 匿名 函数。它没有名字,它的定义就在那里,它写的地方:它有一个参数 x(被推断为 Int),并且 return 是一个 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,是一个例外,"moves" 函数指向下一个外部表达式边界.

那么,让我们看看让你疑惑的案例:

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。它只是接受一个参数,该参数被忽略,不执行任何操作,并且 return 是一个 Unit(类似于 Java 中的 void 类型)。

Unit 是一种表示值无关紧要的事物的类型。一个函数 到 return 的东西,因为这就是函数的作用。当那件事无关紧要时,我们使用 Unit 类型,它只有一个值。 Unit 的唯一值写为 ().

例如,这个 returns true:

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

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