使用高阶函数映射

时间:2015-10-07 06:34:29

标签: scala

这些陈述之间有什么区别?

val numbers = List(1, 2, 3, 4)
numbers map println
// vs.
numbers map (x => println(x))

Scala如何知道 println 会反复处理每个数字

谢谢。

3 个答案:

答案 0 :(得分:5)

  

这些陈述之间有什么区别?

val numbers = List(1, 2, 3, 4)
numbers map println
// vs.
numbers map (x => println(x))

map将一个转换单个元素的函数作为其参数。然后map将此函数应用于每个元素,并返回包含已转换元素的集合。 (BTW,这就是为什么这段代码毫无意义:a)map的返回值被忽略,而b)println总是返回Unit,所以你最终会得到一个Unit的列表。 foreach将是在这里使用的正确方法。)

在第二个示例中,您将λ(匿名的第一类函数对象)传递给map

在第一个示例中,您将完全传递给map?看起来你正在传递一个方法,但这不可能是:a)map接受一个函数,而不是一个方法,而b)方法不是对象,所以你只是不能无论如何都要把它们传过来。你必须以某种方式将方法转换和/或包装到函数中。

在你的第二个例子中,你是非常明确地这样做的:你有一个调用方法的函数。第一个例子更隐含。 Scala有一个名为η-expansion的功能,它允许你“提升”一个方法来运行,这将部分地将该方法应用于其隐式this参数,但保留其他(常规)参数不被绑定。

这是无处不在的下划线的语法:foo.bar _表示“采用bar的方法foo,将其this绑定到foo并转换它成功能“。在这种特殊情况下,您使用println,将其this绑定到顶级REPL上下文对象(这并不重要,因为println实际上并不关心它{ {1}},它像程序一样使用)并将其转换为函数。

但等等,没有下划线?那就对了。如果Scala编译器可以确定您实际上不想调用方法但将其包装到函数中(即,当您不传递参数列表时,该方法需要参数列表,并且上下文需要一个与方法类型兼容的类型),然后Scala将为您执行隐式η-expansion

那么,让我们回到你的问题:

  

这些陈述之间有什么区别?

嗯,一方面,差异就是我上面所描述的:第一个例子使用隐式η-扩展,第二个例子使用显式λ。 OTOH,没有区别:它们都是从this方法创建一个函数,第二个是以更复杂的方式创建它。

  

Scala如何知道 println 会反复处理每个数字

没有。 a)不是println“照顾每个数字”,它是println这样做,而b)Scala什么都不知道,这就是map的编写方式。< / p>

非常简单,map看起来像这样:

map

以下是我们如何使用简单的小清单:

sealed trait MyList[+T] {
  def map[U](function: T ⇒ U): MyList[U]
  def foreach(procedure: T ⇒ Unit)
}

case object EmptyList extends MyList[Nothing] {
  // mapping the empty list returns the empty list
  def map[U](function: Nothing ⇒ U) = EmptyList

  def foreach(procedure: Nothing ⇒ Unit) = ()
}

final case class ListCell[+T](first: T, rest: MyList[T]) extends MyList[T] {
  // mapping a non-empty list returns the result of transforming the first 
  // element of the list and recursively mapping the rest of the list
  def map[U](function: T ⇒ U) = ListCell(function(first), rest map function)

  def foreach(procedure: T ⇒ Unit) = { procedure(first); rest foreach procedure }
}

答案 1 :(得分:3)

map期待一个函数,即该值并生成一个值。在第一个示例中,通过给出lambda显式提供了这样的函数。在第二种情况下,编译器可以检查类型并查看println : A => Unit。请注意,通常情况下,您不希望将map用于生成Unit的函数,这更适合foreach

答案 2 :(得分:2)

这两个语句具有相同的语义,第二个版本只是添加一个间接级别,而这个级别实际上并不需要工作。

问题在于map函数将一个函数作为一个参数,该函数能够在Int上运行并生成一个单元:

final def map[B](f: (A) ⇒ B): List[B]

在你的例子中,A是Int,B是Unit。

现在,如果你看一下println方法的签名和你的匿名函数的签名,你会注意到它们实际上是相同的:

def println(x: Any): Unit
(x => println(x)): Any -> Unit

正在发生的是map函数遍历列表中的元素,对于每个元素x,它调用函数f(x)。由于println和匿名函数都接受Any作为输入映射,因此可以安全地将它们传递给x Int。

使用匿名函数,您只需添加一个中间函数调用步骤。