@EVA001
2017-11-05T07:39:11.000000Z
字数 6470
阅读 390
Scala
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中所有运算都是基于表达式的,求值会有不同策略
1. call by value
对函数实参求值,仅求一次,求得的值直接替换函数中的形式参数
2. call by value
不会对函数实参进行表达式求值,直接把表达式传入函数体内,替换表达式的形参,然后在函数内每次使用到此形参时会被求值
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类型
2. 返回值为一个函数
def funcName() = ( name: String) => {"hello "+name}
参数:name
类型:String => String
返回:String类型
注意上述叫做:匿名函数 - 函数常量 - 函数的文字量(相对于def funcName 叫函数变量)
3. 兼具上述情况
匿名函数没有函数名
定义格式: (形参列表) => { 函数体 }
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