22.13.main方法
Scala的main方法(类似java的static方法)必须定义在一个object内:object Test1 { def main(args: Array[String]){println("hello world")}}
不带命令行参数的简化main方法:object app1 extends Application{ println("hello world")}
Scala的import可以只在局部作用域内生效;“import javax.swing.{JFrame=>jf}”来声明类型的别名。jf.show() limport javax.swing._ import java.util.{List, Map} import java.util._, java.io._ Scala缺省导入如下包: java.lang.* scala.* scala.Predef
由于Scala的package可以是相对路径下定义,有可能命名冲突,可以用:import _root_.java.lang.Long
package com.wr3 { //import java.nio._ // "*"是scala的正常函数名,所以用_
class c1 { def m1() { println("c1.m1()") } } object o1 { def main(args: Array[String]) { println("o1.main()") new c1().m1() } } }
编译:fsc package.scala 运行:java com.wr3.o1 //方式1 scala com.wr3.o1 //方式2
Scala2.8+支持包对象(package object),除了和2.8之前一样可以有下级的object和class,还可以直接有下级变量和函数,例如:package p0 package object p1{ val a = 10 def b = "hello " + a def main(args:Array[String]):Unit = printf("%s",p0.p1.b) }
p1就是一个包对象,a和b就是包p1直属的常量和函数, $fsc foo.scala命令产生如下class: ./p0/p1/package.class 调用: scala p0.p1.package
没有java的: b = (x>y) ? 100 : -1 就用: if (x>y) 100 else -1 没有Java的三元表达式
map m->m ; flatMap m->n ; indices m->m; foreach m->Unit; for (... if ...) yield m->n; collect { case ... if ... => ... } m->n; filter, filterNot m->n; take m->n; takeWhile m->n; forall m->1 (true|false); reduceLeft, foldLeft m->1; scanLeft m->m+1; exists m->1 (true|false); find m->1 (或者None); count m->1; span, partition m->2.
循环中的变量不用定义,如:
for(i<-1 to 10;j=i*i) println(j) for (s <- ss) foo(s) for (i <- 0 to n) foo(i) //包含n,即Range(0,1,2,...,n,n+1) for (i <- 0 until n) foo(i) //不包含n,即Range(0,1,2,3,...,n) 例如: for(n<-List(1,2,3,4) if n%2 == 1) yield n*n // List(1, 9) 等价于不用for的写法: List(1,2,3,4).filter(_%2 == 1).map(n => n*n) for ( n<-Array(1,2,3,4) if n%2 == 1) yield n*n // Array(1, 9)注意:如果if后面不止一条语句,要用{..}包裹。 var s = 0; for (i <- 0 until 100) { s += i } // s = 4950
for条件语句即可用(),也可用{} 如果条件是多行,不能用(),要用{}
for(i<-0 to 5; j<-0 to2) yield i+j // Vector(0, 1,2,1,2, 3,2, 3, 4,3, 4, 5,4, 5, 6,5, 6, 7)
for{i<-0 to 5 j<-0 to2} yield i+j
例子1: //边长21以内所有符合勾股弦的三角形:
def triangle(n: Int) =for{ x <- 1 to21 y <- x to21 z <- y to21ifx * x + y * y == z * z }yield(x, y, z)
结果:// Vector((3,4,5), (5,12,13), (6,8,10), (8,15,17), (9,12,15), (12,16,20))
把每次循环的结果“移”进一个集合(类型和循环内的一致) for {子句} yield {循环体}
正确: for (e<-List(1,2,3)) yield (e*e) // List(1,4,9) for {e<-List(1,2,3)} yield { e*e } // List(1,4,9) for {e<-List(1,2,3)} yield e*e // List(1,4,9)
错误:for (e<-List(1,2,3)) { yield e*e } //语法错误,yield不能在任何括号内
List(1,2,3).foreach(println) ; (1to3).foreach(println); (1until4) foreach println; Range(1,3) foreach println 注意:to包含,until不包含(最后的数)都可以写步长,如:
1 to (11,2) // 1,3,5,7,9,11步长为2 == 1 to 11 by 2
1 until (11,2) // 1,3,5,7,9 == 1 until 11 by 2
val r = (1 to 10 by 4) // (1,5,9), r.start=r.first=1; r.end=10, r.last=9
也可以是BigInt (1:BigInt) to 3
"所有都符合"——相当于A1 && A2&& A3 && ... && Ai && ... && An
(1 to 3) forall (0<) // true (-1 to 3) forall (0<) // false
又如:def isPrime(n:Int) =2 until n forall (n % _ !=0)
for (i<-1 to 100 if isPrime(i)) println(i)
(2 to 20) partition (isPrime _)// (2,3,5,7,11,13,17,19), (4,6,8,9,10,12,14,15,16,18,20)
也可直接调用BigInt的内部方法:
(2to20) partition (BigInt(_) isProbablePrime(10))
//注:isProbablePrime(c)中c越大,是质数的概率越高,10对应概率:1 - 1/(2**10) = 0.999
reduceLeft方法首先应用于前两个元素,然后再应用于第一次应用的结果和接下去的一个元素,等等,直至整个列表。例如 计算阶乘: deffac(n: Int) = 1 to n reduceLeft(_*_) fac(5) // 5*4*3*2= 120 相当于: ((((1*2)*3)*4)*5)
计算sum:List(2,4,6).reduceLeft(_+_) // 12 相当于: ((2+4)+6)
取max:List(1,4,9,6,7).reduceLeft( (x,y)=> if (x>y) x else y ) // 9
或者简化为:List(1,4,9,6,7).reduceLeft(_max_) // 9
相当于:((((1 max 4) max 9) max 6) max 7)
累加或累乘
def sum(L: List[Int]): Int = { var result = 0 for (item <- L) result += item result }
更scalable的写法:
def sum(L: Seq[Int]) = L.foldLeft(0)((a, b) => a + b)
def sum(L: Seq[Int]) = L.foldLeft(0)(_ + _)
def sum(L: List[Int]) = (0/:L){_ + _}//暂时还不理解的请看2.16.6的注释
调用:sum(List(1,3,5,7)) // 16
乘法: def multiply(L: Seq[Int]) = L.foldLeft(1)(_ * _) multiply(Seq(1,2,3,4,5)) // 120 multiply(1 until 5+1) // 120
2.16.6.scanLeft
List(1,2,3,4,5).scanLeft(0)(_+_) // (0,1,3,6,10,15) 相当于:(0,(0+1),(0+1+2),(0+1+2+3),(0+1+2+3+4),(0+1+2+3+4+5))
List(1,2,3,4,5).scanLeft(1)(_*_) // (1,2,6,24,120) 相当于 (1, 1*1, 1*1*2, 1*1*2*3, 1*1*2*3*4, 1*1*2*3*4*5)
注:(z/:List(a, b, c))(op)相当于op(op(op(z, a), b), c) 加法用0,乘法用1 (List(a, b, c):\z) (op) equals op(a, op(b, op(c,z))) 注意两者之间的差异 /: 参数在左 :\参数在右 /:和:\的用法
1 to 10 by 2 take 3 // Range(1, 3, 5) 1 to 10 by 2 drop 3 // Range(7, 9) 1 to 10 by 2 splitAt 2// (Range(1, 3),Range(5, 7, 9))
例子:前10个质数 def prime(n:Int) = (! ((2to math.sqrt(n).toInt) exists (i=> n%i==0)))
2 to 100 filter prime take 10
2.16.8.takeWhile, dropWhile, span
while语句的缩写, takeWhile(...) 等价于:while(...) {take}
dropWhile(...) 等价于:while(...) {drop}
span (...) 等价于:while (...) { take; drop }
1 to 10 takeWhile (_<5) // (1,2,3,4)
1 to 10 takeWhile (_>5) // () //Takes longest prefix of elements that satisfy a predicate.
10 to (1,-1) takeWhile(_>6) // (10,9,8,7)
1 to 10 takeWhile (n=>n*n<25) // (1,2, 3, 4)
如果不想直接用集合元素做条件,可以定义var变量来判断:
例如,从1 to 10取前几个数字,要求累加不超过30:
var sum=0; val rt = (1 to 10).takeWhile(e=> {sum=sum+e;sum<30})// Range(1,2, 3, 4, 5, 6, 7)
注意:takeWhile中的函数要返回Boolean,sum<30要放在最后;
1 to 10 dropWhile (_<5) // (5,6,7,8,9,10)
1 to 10 dropWhile (n=>n*n<25) // (5,6,7,8,9,10)
1 to 10 span (_<5) // ((1,2,3,4),(5,6,7,8,9,10) )
List(1,0,1,0) span (_>0) // ((1), (0,1,0))
注意,partition是和span完全不同的操作
List(1,0,1,0) partition (_>0) // ((1,1),(0,0))
Scala中没有break和continue语法!需要break得加辅助boolean变量,或者用库(continue没有).
例子1:打印'a' to 'z'的前10个 vari=0; val rt = for(e<-('a' to 'z') if {i=i+1;i<=10}) printf("%d:%s\n",i,e)
或者:('a' to 'z').slice(0,10).foreach(println)
例子2:1 to 100和小于1000的数
var (n,sum)=(0,0); for(i<-0 to 100 if (sum+i<1000)) { n=i; sum+=i } // n = 44, sum = 990
例子3:使用库来实现break import scala.util.control.Breaks._
for(e<-1 to 10) { val e2= e*e; if (e2>10)break; println(e) }
注意:其实Scala没有操作符,更谈不上操作符重载;+-/*都是方法名,如1+2其实是(1).+(2)
object operator {class complex(vali:Int,valj:Int) { // val是必须的
def +(c2: complex) = {new complex (i+c2.i, j+c2.j)}
override def toString() = { "(" + i + "," + j + ")" }}
def main(args:Array[String]) = { val c1 = new complex(3, 10) val c2= new complex(5, 70)
printf("%s + %s = %s", c1, c2, c1+c2) }}
编译:fsc operator.scala 运行:java operator // (3,10) + (5,70) = (8,80)
scala._ 自动加载,只有发生类名冲突时才需要带上scala.包名。
scala.AnyValue 所有基本类型的根 Int,Char,Boolean,Double,Unit
scala.AnyRef 所有引用类型的根 相当于java的java.lang.Object
scala.Null 所有引用类型的子类
scala.Nothing 所有全部类型的子类
scala.List 不可变的List scala特色的不可变List
scala.Int scala中可以用int作为别名 Double,Float等类
用途:把一种object类型安全地自动转换成另一种object类型;不改动已有class设计即可添加新的方法;
implicit def foo(s:String):Int = Integer.parseInt(s) //需要时把String->Int def add(a:Int, b:Int) = a+b add("100",8) // 108,先把"100"隐式转换为100
第一步:写函数 def factorial(n: Int) = 1 to n reduceLeft(_*_)
第二步:定义"!"函数 class m1(n: Int) { def ! =factorial(n) }
implicit def m2(n:Int) = new m1(n) //隐式转换,即在需要时把n转换为new m1(n)
注意:上面可以用匿名类简化为:implicit def m2(n:Int) =new { def ! = factorial(n) }
第三步:使用 val n = 100 printf("%d! = %s\n", n, (n!)) // n!相当于new m1(n).!() println(10!)
2.19.3.例子:cout //Converting the receiver
import java.io._
class C1(p:PrintStream) {def << (a:Any) = {p.print(a) p.flush p }}
implicit def anyFuncName(p:PrintStream) = new C1(p)
val endl = '\n' System.out<<"hello"<<" world"<
System.out has no '<<' function, implicitly convert it as C1 object and call the function '<<'
implicit def elvisOperator[T](alt: T) =new{
def ?:[A >: T](pred: A) = if( pred ==null) alt else pred }
null ?: "" // "" "abc" ?: "" // "abc" 10 ?: 0 // 10 (null ?: 0).asInstanceOf[Int] // 0
2.19.5.已有Object加新方法(用法二Converting the receiver)
object NewMethod{ //定义新方法join()
implicit def foo1[T](list:List[T]) = new { def join(s:String) = list.mkString(s) }//测试
def main(args:Array[String]) :Unit= { Console println List(1,2,3,4,5).join(" - ")// " 1 -2- 3 - 4-5"}}
解释:编译器发现List没有join(String)方法,就发查找代码中有没有定义在implicit def xx(List)内的join(String)方法,如果有就调用这个。
为Int增加乘方操作:def pow(n:Int, m:Int):Int = if (m==0) 1 else n*pow(n,m-1) implicit def foo(n:Int) = new {def **(m:Int) = pow(n,m) } 2**10 // 1024
例子2:定义如ruby的10.next
implicit def foo(n:Int) = new { def next = n+1 } 10.next // 11
相当于C语言的类型定义typedef,建立新的数据类型名(别名);在一个函数中用到同名类时可以起不同的别名
例如:type JDate = java.util.Date type SDate = java.sql.Date
val d1 = new JDate() //相当于val d = new java.util.Date()
val d2= new SDate() //相当于val d = new java.sql.Date()
注意:type也可以做泛型
用法三 Implicit parameters,缺省参数
class PreferredPrompt(val preference: String)
object Greeter { def greet(name: String)(implicit prompt: PreferredPrompt ) {
println("Welcome, "+ name +". The system is ready.") println(prompt.preference) }}
implicit val prompt = new PreferredPrompt("Yes, master> ")
scala> Greeter.greet("Joe")
Welcome, Joe. The system is ready.
Yes, master>
def foo[T](a:T) = println("value is " + a) foo(10) // "value is 10" foo(3.14) // "value is 3.14" foo("hello") // "value is hello"
class C1[T] { private var v:T = _ def set(v1:T) = { v = v1 } def get = v } new C1[Int].set(10).get // 10 new C1[String].set("hello").get // "hello"
abstract class C1 { type T val e:T }
abstract class C2 { type T val list:List[T] def len = list.length }
def m1(e1:Int) = new C1{ type T = Int val e = e1 }
def m2(e1:List[Int]) = new C2 { type T = Int val list = e1 }
Console println m1(10) // 10
Console println m2(List(1,2,3,4,5)).len // 5
注意:type也可以做数据类型的alias;C语言中的typedef
Scala没有在语言层面定义Enumeration,而是在库中实现:
例子1:
object Color extends Enumeration{ type Color =Value val RED, GREEN, BLUE, WHITE, BLACK =Value }
注:有点费解,特别是Enumeration defines an inner class named Value
Color.RED // Color.Value = RED
import Color._
val colorful = Color.values filterNot (e=> e==WHITE || e==BLACK)
colorful foreach println // RED\nGREEN\nBLUE
例子2:
object Color extends Enumeration {
val RED =Value("红色")
val GREEN = Value("绿色")
val BLUE = Value("蓝色")
val WHITE = Value("黑")
val BLASK = Value("白")
Color.RED // Color.Value =红色
import Color._
val colorful = Color.values filterNot (List("黑","白") contains _.toString)
colorful foreach println //红色\n绿色\n蓝色