Scala

来自 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

assert –> scala.Predef.assert

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

一样可以用_做任意匹配

===========================================================================

scala中方法和函数的区别

方法的定义:
def m(x:Int):Int = x * 2

函数的定义:

def f= (x: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

你可能感兴趣的:(scala)