scala方法,函数,函数式编程
方法的定义
有以下几点需要注意:
- 所有参数必须制定类型;
- 递归方法,不能省略返回值类型,其他情况下可以忽略(方法可以通过=右侧的函数主体推断出返回值类型)
- 单行方法主体,可以与方法写在一行并忽略{}
过程
在Scala中,定义方法时,如果方法体直接包裹在了花括号里面,而没有使用=连接,则方法的返回值类型就是Unit。这样的方法就被称之为过程。过程通常用于不需要返回值的方法。过程还有一种写法,就是将方法的返回值类型定义为Unit。其实过程就是相当于Java中无返回值的方法。
//将方法主体直接包含在{}中,并省略“=”,其实方法是一个返回Unit类型的方法
scala> def hello(name : String){ print("hello," + name) }
hello: (name: String)Unit
scala> hello("liumingxin")
hello,liumingxin
即使我们在方法中依旧设置了返回值,过程返回值类型依旧是Unit
//即使设置了返回值,过程返回的依旧是Unit类型
scala> def hello(name : String){ print("hello," + name);"hello, " + name}
hello: (name: String)Unit
scala> hello("liumingxin")
hello,liumingxin
懒值(lazy)
在Scala中,提供了lazy值的特性,也就是说,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算。这种特性对于特别耗时的计算操作特别有用。
lazy的调用并不做任何额外的开销,每一次访问lazy值的时候,都会调用一个方法,以线程安全的方式检查该值是否已经被初始化了
scala> val a = 1 / 0
java.lang.ArithmeticException: / by zero
... 28 elided
scala> lazy val a = 1 / 0
a: Int = <lazy>
scala> a
java.lang.ArithmeticException: / by zero
at .a$lzycompute(<console>:11)
at .a(<console>:11)
... 28 elided
很明显,1/0是错误的,在不使用lazy的时候,创建变量直接报错;使用lazy之后,只有调用时才检查是否,如果创建了才进行调用。
在Spark中使用了大量的lazy。
方法VS函数
Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait(Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。) 的类的对象。
Scala 中使用 val 语句可以定义函数,def 语句定义方法
//方法的定义
scala> def m1(x: Int) = x + 1
m1: (x: Int)Int
//函数的定义
scala> val f1 = (x : Int) => x + 1
f1: Int => Int = $$Lambda$1036/961708482@1573e8a5
将函数作为值
scala的语法规定,将函数赋值给变量时,必须在函数后面加上空格和下划线。
//正常声明一个带有一个字符串类型参数,并返回Unit类型的方法
scala> def sayHello(name : String) = println("hello,"+name)
sayHello: (name: String)Unit
//将上述方法转换成一个函数,方法后面加上空格和_转变成函数
scala> val sayHelloFun = sayHello _
sayHelloFun: String => Unit = $$Lambda$1049/1571707504@210a26b
//函数的调用
scala> sayHelloFun("liumingxin")
hello,liumingxin
匿名函数
scala中可以不需要给一个函数命名,就像“y = ax + b”我们该叫它啥?其实很多时候函数并不需要一个名字,名字只是一个代号。
匿名函数的创建规则:(参数 : 参数类型) => 函数体
方法的创建规则:def 名称(参数 : 参数类型) = 函数体
这里再提出方法的定义原因有二:1、回顾方法的创建规则;2、对比函数,理解函数和方法创建时的区别“=”和“=>”需要注意
scala> Array(1,2,3,4).map((x : Int) => x * 3)
res3: Array[Int] = Array(3, 6, 9, 12)
//可以将函数参数使用花括号代替圆括号,这样便于区分
scala> Array(1,2,3,4).map{(x : Int) => x * 3}
res4: Array[Int] = Array(3, 6, 9, 12)
在map函数内部的就是匿名函数
函数的高级用法
将函数作为参数
scala> val hello = (name : String) => println("Hello, " + name)
hello: String => Unit = $$Lambda$1178/531068784@62f3ad90
scala> val hi = (name : String) => println("hi, " + name)
hi: String => Unit = $$Lambda$1179/781449501@1a899fcd
scala> val greeting = (func : (String) => Unit , name : String) => func(name)
greeting: (String => Unit, String) => Unit = $$Lambda$1180/1925117845@1f4d9c7f
scala> greeting(hello,"liumingxin")
Hello, liumingxin
scala> greeting(hi,"liumingxin")
hi, liumingxin
从上面代码可以看出greeting函数中第一个参数是“func : (String) => Unit”,它参数名称为"func",func参数是一个函数,这个函数参数类型为String类型,返回值为Unit类型,只要满足这两个条件,都可以作为greeting函数的第一个参数。因此greeting的类型就是"(String => Unit, String) => Unit"。
将函数作为返回值
将函数作为返回值,我当时学习这块内容的时候是相当不理解,我很疑惑对于这样一段代码:
“def greetingFunc(msg: String) = (name: String) => println(msg + ", " + name)”,又是“=”又是“=>”乱糟糟的,对于刚学scala的人来说这块阅读和使用起来相当混乱,我认为主要是写得太简练了,省略了太多东西后容易让新人过于混乱。接下来我要介绍的就是如何去理解函数作为返回值。
“def greetingFunc(msg: String) = (name: String) => println(msg + ", " + name)”的语法规则如下:
“def 方法名(参数1 : 参数类型) = (参数2 : String) => 函数体”的形式
我们来回想一下方法定义的完整形式:“def 方法名(参数 : 参数类型) : 返回值类型 = {函数体}”,例如:
scala> def greetingFunc(msg : String) : String = {println(msg + ",liumingxin");m
sg}
greetingFunc: (msg: String)String
scala> greetingFunc("hello")
hello,liumingxin
res2: String = hello
上面代码我们先不看方法的返回值,结合“def greetingFunc(msg: String) = (name: String) => println(msg + ", " + name)”来看,“(name: String) => println(msg + ", " + name)”其实就是方法“greetingFunc(msg : String)”的函数体。理解了这一块,我们再接着往下看。
分析一下“(name: String) => println(msg + ", " + name)”,这个东东的形式是不是有点眼熟?是不是就是匿名函数啊?上面有讲。
“匿名函数的创建规则:(参数 : 参数类型) => 函数体”
scala> (name: String) => println("hello, " + name)
res4: String => Unit = $$Lambda$1057/1988962280@147ad4bb
可以看到“(name: String) => println(msg + ", " + name)”匿名函数的类型是"String => Unit",而“greetingFunc(msg : String)”方法的函数体是“(name: String) => println(msg + ", " + name)”,因此“greetingFunc(msg : String)”方法的返回类型就是“String => Unit”,表示:
“greetingFunc(msg : String)”方法返回的是一个带有String类型的参数返回值类型是Unit的函数
“greetingFunc(msg : String)”方法返回的是一个函数,这个函数式一个带有String类型的参数返回值类型是Unit的函数
看到这,我把“def greetingFunc(msg: String) = (name: String) => println(msg + ", " + name)”这种简化形式还原成完整形式
//根据函数的完整定义形式“def 方法名(参数 : 参数类型) : 返回值类型 = {函数体}”定义方法
//匿名函数的创建规则:(参数 : 参数类型) => 函数体
//“{}”中就是greetingFunc(msg : String)方法的函数体
//这个函数体就是“(name : String) => println(msg + "," + name)”,类型是“String => Unit”
//因此完整形式如下:
def greetingFunc(msg : String) : String => Unit = {(name : String) => println(msg + "," + name)}
//完整版及运行结果
scala> def greetingFunc(msg : String) : String => Unit = {(name : String) => println(msg + "," + name)}
greetingFunc: (msg: String)String => Unit
//简化版及运行结果
scala> def greetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
greetingFunc: (msg: String)String => Unit
到这里我们应该能够很清晰的理解函数作为返回值这一高级特性了。一个简单的创建和调用的例子:
scala> def greetingFunc(msg : String)= (name:String) => println(msg + "," + name)
greetingFunc: (msg: String)String => Unit
scala> val helloFunc = greetingFunc("hello")
helloFunc: String => Unit = $$Lambda$1067/709531076@71ae1cb
scala> helloFunc("liumingxin")
hello,liumingxin