Scala高阶函数之间的逐步连接提供了示例

时间:2012-02-26 15:36:42

标签: java scala higher-order-functions

我很难弄清楚如何从Scala高阶函数定义跳转到提供的示例。它是this slide showslide 81上提供的。

这是高阶函数定义:

trait X[A] { def map[B](f: A => B): X[B] }

以下是提供的示例:

(1 to 10) map { x => x * 2 } // evaluates to Vector(2, 4, ..., 20)
(1 to 10) map { _ * 2 }      // shorthand!

咦?我不知道在这里有一些步骤。我得到的例子可能是利用函数定义和一些Scala细节。我只是没有足够的经验阅读Scala并做出连接假设。

我的背景是Java OO。我现在正在学习Scala和函数式编程。这不是第一个我不明白的例子。这只是第一个我觉得我有勇气发帖知道我会看起来无知的人。

我确实试过研究这个。首先,我去了斯卡拉“圣经”,"Programming in Scala 2nd Edition",并尝试从那里了解是否(第165-9页)。然后,我在StackOverflow上搜索了一下。我找到了几个围绕该地区的链接。但是,实际上并没有向我展示Scala高阶函数定义与提供的示例之间的连接,其方式是映射到此幻灯片中的特定实例。

这是我在StackOverflow上找到的内容:

  1. Scala: Workshop Advice
  2. More on generic Scala functions
  3. Scala: How to define "generic" function parameters?
  4. 我现在才意识到我跳过Google并直接进入StackOverflow。嗯。如果你google并找到正确的链接,我很乐意看到它。我没有时间筛选使用像Monkey-monad,blastomorphisms等术语的所有Google链接,同时让我更加困惑,更不可能尝试解决这个问题。

7 个答案:

答案 0 :(得分:6)

高阶函数(或方法)是一个函数/方法,它可以将函数作为参数,也可以将函数作为结果或两者兼顾。

在这种情况下,它是一个名为map的方法,定义在列表,数组以及许多其他类型的容器上。当在1 to 10上调用时,它是一个从1到10的数字范围,由Scala中的Range[Int]表示,它逐个遍历它们并应用函数(已作为参数传入的函数) )到范围内的每个数字。此函数的结果将累积在一个新容器中 - 在这种情况下为Vector[Int],它将作为map方法的结果返回。

(1 to 10) map { x => x * 2 } (1 to 10).map(x => x * 2)x => x * 2的btw语法糖,将1 to 10应用于(1 to 10).map( new Function1[Int, Int] { override def apply(x: Int) = x * 2 }) 的数字。您可以将其视为回叫功能。你也可以这样写:

{{1}}

答案 1 :(得分:6)

我认为以下只是为了显示Scala的一些集合属性而提供的示例签名。特别是它没有显示任何实现,因此您无法真正连接所有点。 它实际上并不符合示例...... 所以,这可能会令人困惑。

trait X[A] { def map[B](f: A => B): X[B] }

我会将其视为:给定X类型元素的集合类A

  • 它具有map函数,该函数在类型B
  • 上进行参数化
  • map函数将f转换为单个A
  • 的函数B
  • map会在X类型的元素上返回相同类型B的集合。

然后跳转到示例以说明使用:

(1 to 10) map { x => x * 2 }

所以,连接点:

  • 集合X的类型为(1到10),此处为Range
  • f: A => Bx => x * 2,推断为Int并返回Int的函数。
  • 鉴于签名,您认为会返回Range而不是Int,但这实际上会返回IndexedSeq

更好的例子可能是:

List(1, 2, 3).map(i => i + "!") // a List[Int]
// returns a List[String]: List("1!", "2!", "3!") 

答案 2 :(得分:5)

让我们用map方法定义一个数据类型,一个单链表。

sealed abstract class MyList[+A] {
  def map[B](f: A => B): MyList[B]  // higher order function declaration.
  def head: A
  def tail: MyList[A]
}
case class Cons[A](head: A, tail: MyList[A]) extends MyList[A] {
  def map[B](f: A => B): MyList[B] = Cons[B](f(head), tail.map(f))
}
case object Nil extends MyList[Nothing] {
  def map[B](f: Nothing => B): MyList[B] = this
  def head = sys.error("head on empty list")
  def tail = sys.error("tail on empty list")
}

列表为空,或者是单个值(head)与列表的其余部分(tail)配对。我们将这两种情况表示为一个类层次结构,从密封的父类MyList扩展而来。

注意Cons#map的实现,我们使用参数f两次,首先用head执行函数,然后传递给{{1的递归调用}}

语法tail.mapf(head)的简写,值f.apply(head)是定义f方法的类Function1的实例。

到目前为止,这么好。 Let.s构建一个列表。

apply

现在,我们希望通过转换每个元素,将val list: MyList[Int] = Cons(1, Cons(2, Nil)) 列表转换为新的Ints列表。在Java中,您将明确地匿名子类String,如下所示。

Function1

这在Scala中是合法的,但信噪比并不高。让我们使用匿名函数语法。

// longhand:
val stringList1: MyList[String] = list.map[String](new Function1[Int, String] {
  def apply(a: Int): String = a.toString
})

我们可以进一步省略显式类型注释,其中编译器有足够的信息来推断它们。

首先,让我们根据val stringList2: MyList[String] = list.map[String]((a: Int) => a.toString) 的元素类型推断出参数a的类型。

list

这些非常简单的函数也可以用占位符语法表示。不要声明参数,只需编写代码并使用val stringList3: MyList[String] = list.map[String](a => a.toString) 表示任何未知数量。这样做,并且还允许推断出_的类型:

stringList4

答案 3 :(得分:3)

让我们专注于你的特质X上定义的 map 方法

def map[B](f: A => B): X[B]

好的,这是一个带有一个参数f的方法。 f(冒号后面的位)的类型为A => B。这是一种功能类型;它是特征Function1[A, B]的简写,但我不想完全考虑这些特征,而是像那样可以为给定的A 值产生一个B。

所以:函数A => B是一个类型为A的单个实例,并生成一个类型为B的实例。以下是一些声明它们的例子

val f = (i: Int) => i.toString //Int => String
val g = (_ : String).length    //String => Int, using placeholder syntax

所以现在想想X[A]是什么; 可以是一个集合类型,例如List。如果您拥有上面的List[String]String => Int函数g,那么您显然可以通过申请获得List[Int]列表中每个元素的函数,构造一个包含结果的新列表。

现在,您可以说:

strings map g //strings is a List[String]

但Scala允许您匿名声明该功能。那是什么意思?嗯,这意味着您可以在内联使用点声明它,而不必将其声明为val或var。这些通常被称为“lambdas”。你困惑的语法是这些函数的两个选项。

strings map { (x: String) => x.length }
strings map { x => x.length }
strings map { _.length }
strings map ( _.length )

这些都基本相同。第一个清楚地声明了传递给map的函数。因为scala有类型推断,所以你可以在这个用例中省略函数输入的类型。使用占位符语法_而不是标识符x,如果您只需要引用一次输入,那么它就是一个很好的糖。除了多语句函数之外,在很多情况下你可以使用parens而不是braces。

答案 4 :(得分:2)

您的示例引用了Scala Collection Framework,它本身具有类型系统的一些复杂用法,可在转换集合时生成最具体的类型。现在,允许这样做的the mechanism很难掌握,并且与示例无关。高阶函数只是a function or method,它作为参数(或返回)other functions。通过添加类型参数并且未提及Scala Collection Frameworks使用implicits,该示例有些模糊。

答案 5 :(得分:2)

不确定你没有得到什么,但要解释这些例子:

trait X[A] { def map[B](f: A => B): X[B] }

trait就像一个Java接口,也可以有具体的方法。

X是特征名称,[A]是类型参数 - 想想Java泛型<A>。 (通常AB等用于集合中的元素类型和T其他地方,但这只是一种约定。)

trait指定一个名为map的成员,该成员是另一个类型参数[B]的方法,并采用类型为A => B的函数参数,返回类型为{{1 }}。这里是抽象的,因为没有方法体。

您可能遗漏的一点是X[B]A => B的缩写。 Function1[A, B]是带有1个参数的函数对象的类型。 Function1(A, B) => C等的缩写。您可以在Java中创建自己的Function2[A, B, C]类型 - 这是一项有趣的练习。函数对象本质上只是一个具有Function方法的函数,从一些参数产生结果。

apply

这些包括无点表示法,(1 to 10) map { x => x * 2 } 写为a.method(b)。因此,a method bto采用RichInt并生成Int的方法。 Rangemap上获取Function1参数的方法(记住,函数只是Range类型的对象)。

Function1也用于编写函数本身(除了类型级别,如上所述)。所以以下是相同的,=>类型的所有对象:

Int => Int

Scala使用类型推断,因此如果存在从上下文中预期的特定函数类型(或任何其他参数化类型),则不需要自己添加所有类型,例如

(x: Int) => x + 1
new Function1[Int, Int] { def apply(x: Int) = x + 1 }
  // note Function1 is a trait, not a class, 
  // so this the same as `new Object with Function[Int, Int]`
new (Int => Int) { def apply(x: Int) = x + 1 }

希望您能看到这个下划线符号的含义。下划线很有用,因为否则总会有一些重复,因为函数定义的RHS必须使用LHS中的参数。另一个例子可以是将val f : Int => Int = x => x + 1 val f : Int => Int = _ + 1 映射到其长度的函数:

String

由于通常推断出val的类型,因此只能使用

提供必要的类型注释
val f: String => Int = _.length

这可能有点混乱,因为语法糖和类型推断意味着有几种方法可以写同样的东西,但是一旦你得到它,你会发现它可以让你的生活更轻松,减少噪音。如果你还没有,可以在REPL中很好地利用它们。

答案 6 :(得分:1)

斯卡拉:(1 to 10) map { x => x * 2 }
英语:取值1到10,然后将每个值乘以2。

有些注意事项:

  • (1到10),Scala认识到这是一个整数集合,特别是Range [Int]。它可以转换为另一种集合类型,例如。 (1 to 10).toList

  • 地图,是小写的。思考动词,将事物映射到另一个事物。

  • {x =&gt; x * 2},被大括号包围。这意味着它是一个没有名称的函数,匿名函数

  • 下划线(_)可以代替x => x


斯卡拉:trait X[A] { def map[B](f: A => B): X[B] }
英语:
  我们定义一个特征,我们可以添加到X类,类型A。
  它有一个方法,它接受一个值并将其映射到新的X类的另一个值。

注意:

  • X[A]X[B]具有相同的集合类型,但可以包含不同类型的元素,例如。 `(1到10).toList map {_.toSTring}将List [Int]映射到List [String]。

  • f: A => B,这意味着map将一个函数作为参数,它有一个A类参数并返回B类。

  • map在所有Scala集合类型中定义。您通常不会自己定义。