来自 fair-jm.iteye.com 转截请注明出处
摘录的一些笔记 来源挺杂的
主要是coursera上 FP principle in Scala课程的笔记
内也许有诸多错误 欢迎指正
避免状态改变
函数是一等公民
求值策略
Call by name:先代入等使用时再求值
Call by value:先求值再代入
如果CBV能终止 那么CBN一定能终止 反过来不成立
例子:
def first(x:Int , y:Int) =x
def loop()=loop
first(1,loop)
那么CBN一定可以终止的 CBV不会
scala用的是CBV
scala也运行用CBN 在定义时用 =>
如下:
def constOne(x:Int,y: => Int) = 1
def —> by-name 使用时求值
val —> by-value 先求值
简单的例子:
def loop:Boolean=loop
用 def x=loop 直接返回
x:Boolean
而用
val x=loop
会因为无限循环而卡住
block:
{}
在block的变量只能在{}内可见
在block的变量会隐藏在{}外的同名变量
;可选 在同一行写多个表达式的时候要用
一个表达式写多行的话用()括起来
或者把运算符写在上一行的结尾
尾递归(tail recursion):
占用的栈空间是固定的 本次结果不依赖下一次
回溯过程调用
用@tailrec注解来说明函数是尾递归的(如果所给函数不是尾递归的 会报错)
构建高阶函数
参数里用
f:Int => Int 的形式 (参数是Int 返回Int)
匿名函数的写法:
(参数) => 函数体
{def f(x1:T1,…,xn:Tn)=E;f}
例子:
def a= (b:Int)=>b //> a: => Int => Int
a(2)
啊哈这里就很简单啦 有名函数用= 匿名函数用 =>
类型的话 就是 (参数) => 返回类型
一个完整的例子:
def f(fun:Int => Int,b:Int):Int= {
def sum(acc:Int,e:Int):Int = {
if(e==0) acc
else sum(fun(e)+acc,e-1)
}
sum(0,b)
} //> f: (fun: Int => Int, b: Int)Int
f((a:Int)=>a*a,2)
curring
函数柯里化
def add(x:Int) = (y:Int) => x+y //> add: (x: Int)Int => Int
add(5) //> res2: Int => Int =
add(5)(1) //> res3: Int = 6
和:
def add(x:Int)(y:Int) = x+y //> add: (x: Int)(y: Int)Int
add(5)_ //> res2: Int => Int =
add(5)(1) //> res3: Int = 6
是等价的
也就是 def 函数名(参数1:类型1)….(参数n:类型n)=E 等同于 def 函数名(参数1:类型1) = (参数2:类型2) => …(参数n:类型n) => E
def add=(x:Int)=>(y:Int) =>(z:Int)=> x+y+z //> add: => Int => (Int => (Int => Int))
等同与
def add(x:Int)(y:Int)(z:Int) = x+y+z //> addx: (x: Int)(y: Int)(z: Int)Int
数据结构:
class Rational(x:Int,y:Int){
def numer = x
def denom = y
}
方法默认是public的
要用私有的 加上private 就可以了
require(条件,”异常时显示”)
不满足条件 抛出异常 字符串是错误时文字
scala中可以作为标识符的:
字母
符号
_
字母以符号结尾
例如:
x1 * +?%& vector_++ counter_=
运算符重载也是遵循原有的优先级:
从低到高如下:
所有的字母
|
^
&
< >
= !
:
+ -
* / %
所有的特殊字符
还有一点要注意的是…
? !的优先级等同于 *
一个完整的例子:
class Rational(x:Int,y:Int){
require(y!=0,"denominator must be nonzero")
def this(x:Int) = this(x,1)
private def gcd(a:Int,b:Int):Int = if (b==0) a else gcd(b,a%b)
private val g=gcd(x,y)
def numer = x / g
def denom = y / g
def <(that:Rational) = numer*that.denom < that.numer*denom
def max(that:Rational) = if(this < that) that else this
def + (that :Rational) =
new Rational(
numer*that.denom+ that.numer*denom,
denom*that.denom
)
def unary_- =
new Rational(
-numer,
denom
)
def - (that:Rational)=
this + -that
def * (that:Rational) =
new Rational(numer*that.numer,denom*that.denom)
override def toString=numer+"/"+denom
}
请注意 对象之间的== 可以重载== 也可以重写equals方法(最好重写equals方法)
抽象类:
abstract class IntSet {
def incl(x:Int):IntSet
def contains(x:Int):Boolean
}
继承依旧使用extends
抽象的方法可以不写override 但不抽象的一定要写override
用Object定义的就是单例
object Empty extends IntSet {
def incl(x:Int):IntSet = new NonEmpty(x,Empty,Empty)
def contains(x:Int):Boolean=false
override def toString=”.”
}
不能用new创建
动态绑定
import中
import week3.Rational
import week3.{Rational,Hello} //导入Rational和Hello
import week3._
默认导入的
scala
java.lang
scala.Predef中的单例
Int –> scala.Int
Boolean –>scala.Boolean
Object –> java.lang.Object
require –> scala.Predef.require
trait
java和scala中都是单继承的
用trait的定义很类似abstract class
直接用trait代替abstract class就可以了
一个类可以实现多个traits 用 with
比interface更强 可以包括域和已实现的方法
trait不能有构造参数和类后面跟的type parameter(抽象类可以有)
===========================================================================
Scala.Any
Scala.AnyVal Scala.AnyRef
基本类型的基类 引用类型的基类
Scala.Nothing Scala.Null
top types:
Any 所有的基类
方法:’==’ ‘!=’ equals hashCode toString
AnyRef 所有引用类型的基类:
java.lang.Object的别名
AnyVal 所有基本类型的基类
底层:
Scala.Nothing是所有类的子类
作用:
1.函数异常返回时 返回Nothing
2.作为空集合的里的元素
Set[Nothing]
Scala抛出异常:
throw Exc
例子:def error(msg:String) = throw new Error(msg) //> error: (msg: String)Nothing
返回值就是Nothing
Null是所有引用类型的子类
null是Null类型的 所以所有的引用类型都可以用null来赋值 记住基本类型中不成立
===========================================================================
多态:
Cons-Lists
Nil
Cons
泛型:
[T]
可用于类也可用于方法
def singletonT = new Cons[T](elem,new Nil[T])
使用:
singletonInt
singletonBoolean
因为scala支持类型推断 所以 也可以写成singleton(1) 没有问题
scala也是会进行泛型擦除的(type erasure)
多态:
subtyping 子类的实例就是基类的实例
generics 泛型
函数作为对象:
A=>B
和Scala.Functional[A,B]:
trait Function[A,B]{
def apply(x:A):B
}
等价关系:
(x:Int) => x*x
def apply(x:Int):Int = x*x
eta-expansion
函数调用 例子:
List(1,2)=List.apply(1,2)
object Function{
def apply[T](x:T):T=x
}
Function[Int](1) //> res0: Int = 1
===========================================================================
Type Bounds:
def assertAllPosS<:IntSet :S=
S<:T 表示 S是T的子类 封顶
S>:T表示 S是T的父类 封底
还有一种限制范围的
[S>:NonEmpty <:IntSet] 以NonEmpty为底 以IntSet为顶的
covariant
这个的意思是 如果List是convariant的
S<:T (S是T的子类)成立
那么List[S]<:List[T]也成立
covariant带来的问题:
NonEmpty[] a=new NonEmpty[]{new NonEmpty(1,Empty,Empty)}
IntSet[] b = a
b[0]=Empty
NonEmpty s=a[0]
Empty不是NonEmpty 若执行肯定会错
这里NonEmpty是Inset的子类 java中Array是covariant的
所以 第二行成立 但Empty不是NonEmpty
java中的解决方法是在创建Array的时候就放入一个表明最初创建类型的Tag
因为Empty不是NonEmpty类型的 所以 b[0]=Empty这句话就会报错(所以是个运行时的错误)
scala中的Array就不是corvariant的:
var x=ArrayAny
var y=ArrayInt
x=y //这里报错 type mismatch; found : Array[Int] required: Array[Any]
里氏替换原则:
Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.
if S<:T then q(x:T)==true ==> q(y:S)==true
如果下A<:B 那么对B能进行的操作 对A也可以进行
或者说对于q(x) x是B类型的能工作 那么q(y) y是A类型的也能工作如果A<:B
也就是 父类型能做的 子类型也要能做
scala:FP OOP
基本类型
Peano numbers
Variance
Scala中List是covariant的 而Array不是
因为List是不可变的 所以不存在一些插入的问题(如上面的java的例子) 而Array不是
关系:
C[T] A B 其中A <: B
若 C[A]<:C[B] C 是 covariant
若 C[A]>:C[B] C 是 cotravariant
如果 C[A]和C[B]没有父子关系 那么C是nonvariant
Scala中是允许你自己定义是否是variant的
class C[+A]{…} C是covariant
class C[-A]{…} C是contrvariant
class C[A] {…} C是nonvariant(默认)
if A2 <: A1 and B1<:B2 ,then
A1 => B1 <: A2 => B2
父类的参数更窄 返回更宽
variance check
因此 Scala会给标识为variant的类做限制:
covariant的类型只能作为返回值
cotrvariant的类型只能作为参数值
scala java
x.isInstanceOf[T] x instanceof T
x.asInstanceOf[T] (T)x
eval
Pattern Matching:
case class
e match{
case pattern => expr
}
在没有匹配项时抛出 MatchError
Pattern可以是:
小写字母开头表示变量(保留字除外) 用于绑定值到这个变量
大写开头表示常量 两者是否相等来进行匹配(==)
_
构造器pattern C(p1,….,pn) 匹配所有C类型(或子类型)通过p1…p构造的
(注意类的定义要用case class)
同样的变量只能在Pattern出现一次
顺序匹配下去
例子:
trait Expr {
def isNumber: Boolean
def isSum: Boolean
def numValue: Int
def leftOp: Expr
def rightOp: Expr
def show():String = this match{
case Number(n) => n+""
case Sum(e1,e2) => e1+"+"+e2
}
}
另一个例子:
def show(e: Expr): String = e match { case Sum(Prod(x1, x2), Prod(x3, x4)) => if (x1 == x3) show(x1) + "*" + "(" + show(x2) + "+" + show(x4) + ")" else if (x1 == x4) show(x1) + "*" + "(" + show(x2) + "+" + show(x3) + ")" else if (x2 == x3) show(x2) + "*" + "(" + show(x1) + "+" + show(x4) + ")" else if (x2 == x4) show(x2) + "*" + "(" + show(x1) + "+" + show(x3) + ")" else show(_Prod(x1, x2)) + "+" + show(_Prod(x3, x4)) case Sum(n1, n2) => show(n1) + "+" + show(n2) case Prod(Sum(n1, n2), n3) => "(" + show(_Sum(n1, n2)) + ")" + "*" + show(n3) case Prod(n3, Sum(n1, n2)) => show(n3) + "*(" + show(_Sum(n1, n2)) + ")" case Prod(n1, n2) => show(n1) + "*" + show(n2) case _ => e.toString() }
Lists:
List(“xxx”,”xxx”) List[String]
List() List[Nothing]
List(1,2,3,4,5)
:: 是 scala的cons操作符
car :: cdt
“1”::(“2”::(“3”::Nil)) //> res1: List[String] = List(1, 2, 3)
scala中做了简化 所以 也可以写成”1”::”2”::”3”::Nil是等价的 (::其实是方法名 相当于调用方法 可以写成.::)
1 :: 2 :: 3 :: 4 :: Nil 和 Nil.::(4).::(3).::(2).::(1)
等价
List中一样有 head和tail方法
此外 两个List连接可以用:::
List(1,2,3):::List(4,5,6) //> res6: List[Int] = List(1, 2, 3, 4, 5, 6)
这个和List(4,5,6).:::(List(1,2,3))等价
List的模式:
Nil 表示空list
p :: ps 表示以p开头的元素 这个模式和[Head|Tail]一致
List(p1,…,p2) 也可以写成 p1::….::pn::Nil
当然也可以用_
例子:
List(1,2,3) match {
case List(1,_,3) => “yes” //这样其实也是一种构造模式吧
} //> res2: String = yes
更多用于List的函数:
xs.length
xs.last 获得最后一个
xs.init 得到一个除了最后一个元素的列表
xs take n
xs drop n
xs(n)
Console代码
scala> def xs= List(0,1,2,3,4)
xs: List[Int]
scala> xs.length
res0: Int = 5
scala> xs.last
res1: Int = 4
scala> xs.init
res2: List[Int] = List(0, 1, 2, 3)
scala> xs take 1
res3: List[Int] = List(0)
scala> xs drop 1
res4: List[Int] = List(1, 2, 3, 4)
scala> xs(0)
res5: Int = 0
创建新列表:
xs ++ ys 合并
xs.reverse 倒序
xs.updated(n,x)
查找元素:
xs indexOf x 不存在返回-1
xs contains x
tuple:
pair: (x,y)
scala> val (label,value) = (1,”s”)
label: Int = 1
value: String = s
scala> (1,”s”)
res0: (Int, String) = (1,s)
超过两个元素的就是tuple了
(T1,….,Tn)是Scala.Tuplen[T1,…,Tn]的缩写
(e1,….,en)是Scala.Tuplen(e1,…,en)的缩写
tuple模式中也和上面的(e1,…,en)一样
implicit:
在函数的参数中使用 编译器会根据类型得到正确的隐含参数
The compiler will ?gure out the right implicit to pass based on the demanded type.
def msortT(implicit ord:Ordering)
调用时只需:
msort(List(1,2,3)) 或者msort(List(“1”,”2”,”3”))
Ordering在scala.math包下 提供了常用的比较操作等
如何查找的:
? is marked implicit
? has a type compatible with T
? is visible at the point of the function call, or is de?ned in a
companion object associated with T.
以上的原则 Ordering是包含类型参数的 完整的形式是 Ordering[T]
所以编译器在使用List(1,2,3)时会正确找到Ordering[Int] 而为List(“1”,”2”,”3”)时则为Ordering[String]
def lessT(implicit ord:math.Ordering[T]):Boolean=ord.lt(a, b)
less(1,2)
List的高阶函数
map
filter
span
takeWhile
dropWhile
fold/reduce:
List(x1,…,xn) reduceLeft op = (…(x1 op x2) op…) op xn
((x,y) => x*y)可以写成(*)
reduceLeft不能处理Nil 一般用foldLeft代替使用
(List(x1,…,xn) foldLeft z)(op) = (..(z op x1) op…) op xn
def sum(xs:List[Int]) = (xs foldLeft 0)(+)
以及foldRight
注意foldLeft和foldRight并不一定能通用
原因在于
foldLeft中 累加器在参数左
foldRight中 累加器在参数右
累加器的类型是U List的类型是T
foldLeft是:
U T
U T
U T
而foldRight则是
T U
T U
U T
也就是一个操作符的问题 例如:: 左边是要T的 而右边是List[T] 注意一下缩写的op函数就可以了
foldLeft中累加器是第一个参数 元素是第二个参数
foldRight中累加器是第二个参数 元素是第一个参数
其他的集合类:
都是不可变的
Vector 用Vector()c创建 +: :+ x+:xs xs:+x
默认32大小 如果不够用了 会增加一层 log32(N)
List和Vector的基类是Seq 所有sequences的基类
Seq是Iterable的子类
Iterable
Seq Set Map
String和Array也类似Seq(但不是子类) 他们一样可以用map fold 等方法
Ranges:
val r:Range = 1 until 5
val 3:Range = 1 to 5
1 to 10 by 3
6 to 1 by -2
Range中的域只有 上限 下限 步长
Range也可以用map fold等方法
for:
for (s) yield e //yield是将后面内容输出为一个List
s中可以写赋值(必须在第一项)和判断
如果有多个赋值 可以写成 {}的形式
mkString 把元素通过制定的字符连接成字符串 和groovy的join有点类似
for的转换:
for(x <- e1) yield e2 被转换成 e1 map(x=>e2)
等等
for的转换是基于 map flatMap withFilter的
ScalaQuery Slick
Map:
构建用Map(key -> value)
取出用get
如果不存在返回None 不然返回Some(x) 用模式【匹配获取
List: sortWith sorted
groupBy 返回的是Map
1 ->2 和 (1,2)是一样的:
scala> 1 -> 2
res5: (Int, Int) = (1,2)
scala> (1,2)
res6: (Int, Int) = (1,2)
scala> Map((1,2))
res7: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
默认值:
用withDefaultValue方法 后面跟一个defaultValue 构建一个新的Map
Scala用*表示不定参数函数 例如:
def this(bindings:(Int,Double)*)=???
对于合适的数据 可以用toMap toList进行相互的转换
Map也可以作为函数 参数是key 返回值是value
操作总结:
Map:
+ map和元素之间
++ map和map之间 如果有相同的key 则以第二个map为准
withDefaultValue 返回一个对于不存在key 有默认值的Map
1 -> 2 和 (1,2)等价
符合结构的Map和List可以互相转换
List:
元素 :: List cons构造(往前追加一个元素)
List ++ List 合并
List ::: List 合并
Stream:
构造 可以用Stream.empty 用Stream.cons方法进行构造
可以用 Stream(el1,…,eln)进行构造
也可以用 其他Seq的toStream方法进行构造
scala> Stream(1,2)
res0: scala.collection.immutable.Stream[Int] = Stream(1, ?)
注意后面是个? 第二个或者后面的还没用到就先不求值 结构和List是类似的
Stream基本支持List的方法 但有一个例外 Stream不支持 head :: tail 不过有另外一个选择 就是使用 #:: 同样:Stream.cons(x,xs)和 x #:: xs 一样
Lazy Evaluation 惰性求值
和 by-name 求值(每次用到的时候再求值) 和 strict求值 (先求值再使用) 不同
惰性求值在使用时计算并且以后使用的话用已计算好的值
scala支持惰性求值 使用lazy val 定义就可以了
scala> def eval_():Int = {
| println("evalution")
| 1
| }
eval_: ()Int
scala> def pp(i: =>Int) ={
| println(i);println(i);
| }
pp: (i: => Int)Unit
scala> pp(eval_()) // by-name求值
evalution
1
evalution
1
scala> lazy val x=eval_() //惰性求值
x: Int = <lazy>
scala> x
evalution
res11: Int = 1
scala> x
res12: Int = 1
scala> def y =eval_() //def也是by-name求值
y: Int
scala> y
evalution
res13: Int = 1
scala> y
evalution
res14: Int = 1
当然scala默认用的是strict求值
例子:
scala> def expr = {
| val x = {print("x");1}
| lazy val y = { print("y");2}
| def z = {print("z");3}
| z+y+x+z+y+x
| }
expr: Int
scala> expr
xzyzres15: Int = 12
Stream的cons就是使用的惰性求值:
scala> val s = Stream.cons(1,Stream({println("x");2}))
s: Stream.Cons[Int] = Stream(1, ?)
scala> s(1)
x
res21: Int = 2
scala> s(1)
res22: Int = 2
用Stream可以构建无穷的数据(需要时取一部分就可以了)
一些语法:
循环:
while用法和java一样
for的语法是
for(i <- 初始值 until 终止值){
}
输出0-9:
for(i <- 0 until 10){ println(i) }
还可以用forEach
List(1,2,3,4,"string").foreach( i => println(i))
范围和元组
范围用
val ran= 初始值 until 终止值
默认步长为1 修改步长用 () by 步长:
val range=0 until 10 by 2 //> range : scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
range.size //> res1: Int = 5
range.start //> res2: Int = 0
range.end //> res3: Int = 10
range.step //> res4: Int = 2
或者用 to(包含终点)
val range=0 to 10 by 2 //> range : scala.collection.immutable.Range = Range(0, 2, 4, 6, 8, 10)
元祖依旧是用()
用._1取第一个元素以此类推 元祖可以用来进行多值赋值:
val (x,y)=(1,2) //> x : Int = 1
//| y : Int = 2
val tuple=(1,2) //> tuple : (Int, Int) = (1,2)
tuple._1 //> res1: Int = 1
一样可以用_做任意匹配
方法的定义:
def m(x:Int):Int = x * 2
函数的定义:
有参数的方法不能写成 m 而函数则没有此限制:
scala> f
res0: Int => Int = <function1>
scala> m
<console>:9: error: missing arguments for method m;
follow this method with `_' if you want to treat it as a partially applied function m ^
===========================================================================
方法没有参数时可以不写() 而函数一定要写():
scala> def m = 2
m: Int
scala> def f = =>2
<console>:1: error: illegal start of simple expression
def f = =>2
^
当然方法写了也可以:
scala> def m()= 2 m: ()Int
这样的情况下用 m 其实是调用了方法 而对于f 如果直接f则是返回函数本身
===========================================================================
Scala中含有 ETA expansion
所以可以将传递函数的地方直接传递方法 但是不能用于赋值:
scala> val myList = List(3, 56, 1, 4, 72)
myList: List[Int] = List(3, 56, 1, 4, 72)
scala> myList.map((x)=>2*x) res1: List[Int] = List(6, 112, 2, 8, 144) scala> def m3(x: Int) = 3*x m3: (x: Int)Int scala> myList.map(m3) res2: List[Int] = List(9, 168, 3, 12, 216)
===========================================================================
方法强转为函数 使用_就可以了:
scala> def m4(x: Int) = 4*x
m4: (x: Int)Int
scala> val f4 = m4 _ //注意这边有个空格 但如果是方法做柯里化有个()就不用多加一个空格(加了也没关系)
f4: Int => Int = <function1>
scala> f4(2)
res3: Int = 8
scala>
===========================================================================
http://java.dzone.com/articles/revealing-scala-magician%E2%80%99s 写道
http://java.dzone.com/articles/revealing-scala-magician%E2%80%99s 写道
call-by-name的参数一定是方法而不是函数
//use “x” twice, meaning that the method is invoked twice.
scala> def m1(x: => Int) = List(x, x)
m1: (x: => Int)List[Int]
scala> import util.Random
import util.Random
scala> val r = new Random()
r: scala.util.Random = scala.util.Random@ad662c
//as the method is invoked twice, the two values are different.
scala> m1(r.nextInt)
res37: List[Int] = List(1317293255, 1268355315
翻译自:
http://java.dzone.com/articles/revealing-scala-magician%E2%80%99s
一些学习资料:
http://zh.scala-tour.com/#/welcome
http://twitter.github.io/scala_school/zh_cn/
http://twitter.github.io/effectivescala/index-cn.html