我很难弄清楚如何从Scala高阶函数定义跳转到提供的示例。它是this slide show在slide 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上找到的内容:
我现在才意识到我跳过Google并直接进入StackOverflow。嗯。如果你google并找到正确的链接,我很乐意看到它。我没有时间筛选使用像Monkey-monad,blastomorphisms等术语的所有Google链接,同时让我更加困惑,更不可能尝试解决这个问题。
答案 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 => B
为x => 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.map
是f(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>
。 (通常A
,B
等用于集合中的元素类型和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 b
是to
采用RichInt
并生成Int
的方法。 Range
是map
上获取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集合类型中定义。您通常不会自己定义。