Scala语言的特性
Scalable语言
Scala是一门可伸缩的scalable语言,既可以写复杂的服务器端程序,也可以写简单的脚本
纯正的面向对象
所有的概念最终都会被时限为纯正的对象
函数式编程的特性
函数式程序思想!!!
无缝的Java互操作
构建于Jvm之上,Java的包可以在Scala中使用,huo1Scala写好的程序给Java调用
编程思路灵活
既可以面向对象的思想,也可以函数式编程的思想
Scala之父:Martin Odersky
导读:
函数式变成的概念和思想
Scala的开发环境搭建
Scala语言的基础
Scala中的类型和求值策略
Scala中函数的概念
Immutable Collections如何用函数式思想实现数据结构和其上的一些操作
函数式编程思想
只用纯函数编程
定义:函数式编程是一种编程范式,构建计算机程序和结构的方法和风格,把计算当做数学函数求值的过程,并且避免了改变状态和可变的数据
纯函数特点 Pure Function
纯函数,没有副作用的函数
没有副作用:状态的变化
例如:调用 def Add(y:Int) = x + y
其结果为xy之和,并且调用之后没有引起x值的变换,没有副作用
所以,Add函数没有副作用
引用透明性
对于上述Add函数,对于同一输入y,返回结果均相同
所以,Add具有引用透明性
如何确保引用透明
不变性Immutablity:任何的状态和值都是不变的,才能获得引用透明
函数与变量,对象类是同一级的,即函数中可以定义函数,有变量的地方都可以使用函数,都是等同的
高阶函数
函数作为一个函数的输入或另一个函数的输出
闭包 closure
表达式求值
函数式编程中,一切都是表达式,表达式求值策略:
严格求值:call by value
非严格求值:call by name
惰性求值
定义表达式时不会立即求值,只在第一次调用时才求值
递归函数
函数式编程中没有循环语句,全部的循环用递归实现
调优递归:尾递归
函数式编程的优点
Lisp是第一种函数式编程语言
- 编程代码量少
- 当构造完含数之后,对于相同输入,输出相同,便于调试
- 非常适用于并行编程,没有副作用,具备引用透明性,在n个节点运算结果是相同的
- 传统语言多核编程非常复杂
Scala环境的搭建
安装Jdk6以上,并安装Scala包
Scala基础语法
变量修饰符
- val 定义 immutable variable 常量
- var 定义 mutable variable 变量
- lazy val 惰性求值的常量
定义时不用显示的说明类型,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 = 常量或变量组成的表达式
Scala的类体系
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的子类)
- NumericTypes
对于数值类型:低精度可以赋值给高精度,反之不行,数据会缺失:报类型不匹配错误 - Unit
往往作为函数的返回值出现,表明函数有副作用 - Null
表示一个引用类型的值为空。通常不使用 - Nothing
对于函数而言,如果返回值为nothing,那么则表示函数异常
scala> def foo() = throw new Exception(“1111”)
foo: ()Nothing - String
新特性 - 字符串插值(interpolation)
scala> val name=”Jack”
name: String = Jack
scala> s”my name is $name” //使用字符串插值
res11: String = my name is Jack
代码块Block
代码块用于组织多个表达式:{exp1;exp2}
多个表达式在一行2时需要分号分割,代码块本事也是一个表达式
最后的求值,是最后一个表达式
函数
定义函数的方式:
def functionName(param:paramType):returnType = {
//body expressions
}
示例:
1 | object worksheetA { |
scala中的if
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
<< 更多精彩尽在『程序萌部落』>>
<< https://www.cxmoe.com >>
scala中的for comprehension
用于实现循环的一种推导式,本身是由map() reduce()组合实现的
是scala语法糖(thin text sugar)的一种
1 | for{ |
示例:
1 | object worksheetA { |
scala中的try
try也是一个表达式,返回一个值
1 | try{ |
scala中的macth
类似switch,但也是一个表达式,返回相应的值,主要用在 pattern match
1 | var expression = 1 //> expression : Int = 1 |
Scala的求值策略
scala中所有运算都是基于表达式的,求值会有不同策略
- call by value
对函数实参求值,仅求一次,求得的值直接替换函数中的形式参数
- call by value
不会对函数实参进行表达式求值,直接把表达式传入函数体内,替换表达式的形参,然后在函数内每次使用到此形参时会被求值
scala通常使用call by value
def foo(x: Int) = x //call by Value
def foo(x: => Int) = x //call by Name
下面是两种求值策略在不同情况下的运行机制:
1 | def add(x: Int,y: Int) = x * x def add(x: =>Int,y: =>Int) = x * x |
注意上述运行机制的区别
1 | scala> def bar(x:Int, y: => Int) : Int = 1 |
进行函数设计和调用时,两种差异要搞清楚
Scala中的函数
- 支持把函数作为实参传递给另外一个函数
- 支持把函数作为返回值
- 支持把函数赋值给变量
- 支持把函数存储在数据结构里
即,在scala中,函数跟普通变量一样使用,且具有函数的相关类型
函数的类型
在scala中,函数类型的格式为 A => B,表示一个:接受参数类型为A的、并返回类型B的函数
eg:
Int => String 是把整型映射为字符串的函数类型
高阶函数
- 接受的参数为函数参数:f
1
2
3def funcName( f: (Int, Int) => Int) = {
f(4,4)
}
类型:Int => Int
返回:Int类型 - 返回值为一个函数参数:name
1
def funcName() = ( name: String) => {"hello "+name}
类型:String => String
返回:String类型
注意上述叫做:匿名函数 - 函数常量 - 函数的文字量(相对于def funcName 叫函数变量) - 兼具上述情况
匿名函数
匿名函数没有函数名
定义格式: (形参列表) => { 函数体 }
- (x: Int) => { x * x }
- (x: Int,y: Int) => { x + y }
- var add = (x: Int,y: Int) => { x + y }
- def func() = (x: Int,y: Int) => { x + y }
1
2
3
4
5
6
7
8
9
10
11
12
13
14scala> (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中的重要的技术,具有多个参数的函数转化成一个函数列,每个函数只有单一参数
1 | def add(x: Int,y: Int) = { x + y } //普通函数定义 |
解释:curriedAdd(1)_,下划线统配之后的全部参数列表,此处a=1固定,只有b是可变值,下划线通配变量b
add(2),传入curriedAdd后a=1,b=2
利用柯里化技术,通过原有通用函数构造一些新的函数
Scala中的递归
scala里计算n的阶乘
1 | def factorial(n: Int): Int = |
递归优化:变成尾递归,尾递归会复写当前栈,不会导致堆栈溢出
尾递归优化:用¥annotation.tailrec显示指明编译时进行尾递归优化
1 | @annotation.tailrec |
上述引入m,m保留当前运算之前的历史阶乘结果
如果退出,则就是递归的值,如果不退出,那么把当前结果传入下一次,这样不需要开辟栈保留计算结果,每次只需m变量记录结果
示例:求f(x)在(a,b)上的和
1 | def sum(f: Int => Int)(a: Int)(b: Int): Int = { |
😒 留下您对该文章的评价 😄