Scalable语言 Scala是一门可伸缩的scalable语言,既可以写复杂的服务器端程序,也可以写简单的脚本 纯正的面向对象 所有的概念最终都会被时限为纯正的对象 函数式编程的特性 函数式程序思想!!! 无缝的Java互操作 构建于Jvm之上,Java的包可以在Scala中使用,huo1Scala写好的程序给Java调用 编程思路灵活 既可以面向对象的思想,也可以函数式编程的思想
导读: 函数式变成的概念和思想 Scala的开发环境搭建 Scala语言的基础 Scala中的类型和求值策略 Scala中函数的概念 Immutable Collections如何用函数式思想实现数据结构和其上的一些操作
定义:函数式编程是一种编程范式,构建计算机程序和结构的方法和风格,把计算当做数学函数求值的过程,并且避免了改变状态和可变的数据
纯函数,没有副作用的函数
例如:调用 def Add(y:Int) = x + y 其结果为xy之和,并且调用之后没有引起x值的变换,没有副作用 所以,Add函数没有副作用
对于上述Add函数,对于同一输入y,返回结果均相同 所以,Add具有引用透明性
不变性Immutablity:任何的状态和值都是不变的,才能获得引用透明 函数与变量,对象类是同一级的,即函数中可以定义函数,有变量的地方都可以使用函数,都是等同的
函数作为一个函数的输入或另一个函数的输出
函数式编程中,一切都是表达式,表达式求值策略:
严格求值:call by value 非严格求值:call by name
定义表达式时不会立即求值,只在第一次调用时才求值
函数式编程中没有循环语句,全部的循环用递归实现 调优递归:尾递归
Lisp是第一种函数式编程语言
安装Jdk6以上,并安装Scala包
定义时不用显示的说明类型,scala会自己进行变量推导
前两种定义,在定义时表达式就会立即求值 lazy val 在REPL中,scala会给没有变量名的变量自动取值resN,可以直接引用已有的resN 注意: scala中不允许常量定义后被直接改变,而变量var可以 val x = 10 x = 20 //会报错reassignment to val val x = 20 //正确的重新赋值,需要使用val重新定义 对于lazy val,注意没有lazy var,一般是定义惰性求值的表达式 val l = 常量或变量组成的表达式
Any 所有类的父类 AnyVal 值类型 NumericTypes 数值类型 Byte,Shot,Int,Long,Float,Double Boolean 布尔类型 Char 字符类型 Unit 空类型,相当于Java的void AnyRef 所有引用类型的父类 All java. ref types 所有Java的引用类都是其子类 All scala. ref types 所有自定义的scala的类都是其子类 Null 所有引用类型的最后一个子类 Nothing 所有类型的最后一个子类(既是AnyVal又是AnyRef的子类)
代码块用于组织多个表达式:{exp1;exp2} 多个表达式在一行2时需要分号分割,代码块本事也是一个表达式 最后的求值,是最后一个表达式
定义函数的方式: def functionName(param:paramType):returnType = { //body expressions } 示例:
object worksheetA {
// 完整形式
def Hello(name:String):String = {
s"Hello,$name"
} //> Hello: (name: String)String
Hello("Jack") //> res0: String = Hello,Jack
// 省略返回值,自动推断类型
def Hello2(name:String) = {
s"Hello,$name"
} //> Hello2: (name: String)String
Hello2("Tom") //> res1: String = Hello,Tom
// 单语句的函数体
def add(x:Int,y:Int) = {
x + y
} //> add: (x: Int, y: Int)Int
// 可以省略大扩号
//def add(x:Int,y:Int) = x + y
add(1,2) //> res2: Int = 3
}
if是表达式,而不是语句 if(逻辑表达式) valA else valB val a = 1 //> a : Int = 1 if(a!=1) “not none” //> res3: Any = ()返回空 if(a!=1) “not none” else a //> res4: Any = 1
用于实现循环的一种推导式,本身是由map() reduce()组合实现的 是scala语法糖(thin text sugar)的一种
for{
x <- xs
y = x + 1
if( y > 0)
}yield y
示例:
object worksheetA {
//初始化一个List
val list = List("alice","bob","cathy")
for (
//遍历list每一个元素给s,generator
s <- list
)println(s)
for {
s <- list
//串长度大于三才被打印
if( s.length > 3)
}println(s)
val res_for = for{
s <- list
//变量绑定,variable binding
s1 = s.toUpperCase()
if ( s1 != "")
//yeild导出的意思,如果每次s1不空,则生成新的collection
}yield (s1)
}
try也是一个表达式,返回一个值
try{
Integer.praseInt("dog")
}catch{
case _ => 0 //下划线是通配符,统配所有异常
}finally{
print("总是会打印");
}
类似switch,但也是一个表达式,返回相应的值,主要用在 pattern match
var expression = 1 //> expression : Int = 1
expression match{
case 1 => "dog"
case 2 => "cat"
//类似switch的default
case _ => "others"
} //> res5: String = dog
scala中所有运算都是基于表达式的,求值会有不同策略
def foo(x: Int) = x //call by Value def foo(x: => Int) = x //call by Name
下面是两种求值策略在不同情况下的运行机制:
def add(x: Int,y: Int) = x * x def add(x: =>Int,y: =>Int) = x * x
add(3+4,7) add(3+4,7)
=>add(7,7) =>(3+4)*(3+4)
=>7*7 =>7*(3+4)
=>49 =>7*7
=>49
add(7,3+4) add(7,3+4)
=>add(7,7) =>7*7
=>7*7 =>49
=>49
注意上述运行机制的区别
scala> def bar(x:Int, y: => Int) : Int = 1
bar: (x: Int, y: => Int)Int
scala> def loop():Int = loop
loop: ()Int
scala> bar(1,loop)
//loop函数位于的参数的定义方式是y: => Int,即call by name,不进行求值,会带到函数体内并且使用时
才求值,此处,loop没有机会执行。
res0: Int = 1
scala> bar(loop,1)
//loop函数位于的参数的定义方式是y: Int,即call by value,会直接将表达式求值并代替形参,此处loop
首先被执行求值,故而陷入死循环。
输出:死循环
进行函数设计和调用时,两种差异要搞清楚
即,在scala中,函数跟普通变量一样使用,且具有函数的相关类型
在scala中,函数类型的格式为 A => B,表示一个:接受参数类型为A的、并返回类型B的函数 eg: Int => String 是把整型映射为字符串的函数类型
def funcName( f: (Int, Int) => Int) = {
f(4,4)
}
参数:f 类型:Int => Int 返回:Int类型
返回值为一个函数
def funcName() = ( name: String) => {"hello "+name}
参数:name 类型:String => String 返回:String类型 注意上述叫做:匿名函数 - 函数常量 - 函数的文字量(相对于def funcName 叫函数变量)
匿名函数没有函数名 定义格式: (形参列表) => { 函数体 }
(x: Int,y: Int) => { x + y }
var add = (x: Int,y: Int) => { x + y }
scala> (x: Int,y: Int) => { x + y }
res0: (Int, Int) => Int = $$Lambda$1016/2093139281@69feb4d9
scala> var add = (x: Int,y: Int) => { x + y }
add: (Int, Int) => Int = $$Lambda$1025/1152113439@62108cd3
scala> add(1,2)
res1: Int = 3
scala> def funcName() = ( name: String) => {"hello "+name}
funcName: ()String => String
scala> funcName()("Jack")
res4: String = hello Jack
Scala中的重要的技术,具有多个参数的函数转化成一个函数列,每个函数只有单一参数
def add(x: Int,y: Int) = { x + y } //普通函数定义
def add(x: Int)(y: Int) = { x + y } //柯里化函数定义,多个参数单一化,串接起来
def curriedAdd(a: Int)(b: Int) = a + b
curriedAdd(2)(2) //4
val add = curriedAdd(1)_ //Int => Int
add(2) //3
解释:curriedAdd(1)_,下划线统配之后的全部参数列表,此处a=1固定,只有b是可变值,下划线通配变量b add(2),传入curriedAdd后a=1,b=2
利用柯里化技术,通过原有通用函数构造一些新的函数
scala里计算n的阶乘
def factorial(n: Int): Int =
if(n <= 0) 1
else n * factorial(n - 1)
递归优化:变成尾递归,尾递归会复写当前栈,不会导致堆栈溢出 尾递归优化:用¥annotation.tailrec显示指明编译时进行尾递归优化
@annotation.tailrec
def factorial(n: Int,m: Int): Int =
if(n <= 0) m
else factorial(n - 1, m * n)
factorial(5,1)
上述引入m,m保留当前运算之前的历史阶乘结果 如果退出,则就是递归的值,如果不退出,那么把当前结果传入下一次,这样不需要开辟栈保留计算结果,每次只需m变量记录结果 示例:求f(x)在(a,b)上的和
def sum(f: Int => Int)(a: Int)(b: Int): Int = {
@annotation.tailrec
def loop(n: Int, acc: Int): Int = {
//函数中可定义其他函数
//n:循环变量
//acc:累积和
if (n > b) {
println(s"n=${n}, acc=${acc}")
acc
//if表达式返回acc的值
} else {
println(s"n=${n}, acc=${acc}")
loop(n + 1, acc + f(n))
//else表达式返回loop
}
}
loop(a, 0)
} //> sum: (f: Int => Int)(a: Int)(b: Int)Int
//函数y=f(x)
sum(x => x)(1)(5) //> n=1, acc=0
//| n=2, acc=1
//| n=3, acc=3
//| n=4, acc=6
//| n=5, acc=10
//| n=6, acc=15
//| res0: Int = 15
sum(x => x * x)(1)(5) //> n=1, acc=0
//| n=2, acc=1
//| n=3, acc=5
//| n=4, acc=14
//| n=5, acc=30
//| n=6, acc=55
//| res1: Int = 55
sum(x => x * x * x)(1)(5) //> n=1, acc=0
//| n=2, acc=1
//| n=3, acc=9
//| n=4, acc=36
//| n=5, acc=100
//| n=6, acc=225
//| res2: Int = 225
//利用柯里化技术,通过原有通用函数构造一些新的函数,简化代码
val sumSquare = sum(x => x * x)_
//> sumSquare : Int => (Int => Int) = sumfunc$$$Lambda$13/2054798982@34ce8af7
sumSquare(1)(5) //> n=1, acc=0
//| n=2, acc=1
//| n=3, acc=5
//| n=4, acc=14
//| n=5, acc=30
//| n=6, acc=55
//| res3: Int = 55