admin管理员组

文章数量:1794759

Scala语言初入门

Scala语言初入门

Scala介绍

  • Scala语言来自于Scalable(可伸缩的)既可以写一些小的脚本,又可以写一写复杂的服务器端的程序。
  • Scala是一门纯正的面向对象语言,具有OO特征。
  • Scala是一门函数式语言,可以用函数式思想来编写代码
  • Scala是构建于JVM之上的,与java可以无缝互操作。
函数式编程思想 函数式编程基本名词介绍

纯函数来编写程序。

  • 纯函数(Pure Function),或函数的纯粹性(Purity)没有副作用(side Effect)
  • 副作用是状态的变化
    • 例子
    • 修改全局变量
    • 抛出异常
    • IO读写
    • 调用有副作用的函数
var x = 1 def XplusY_V1(y:Int) = x + y def XplusY_V2(y:Int) = {x += y;x}

上面第一个函数是输入一个int类型的y,第二个函数是输入一个int类型的y返回值是x。 第二个函数相对于第一个函数对变量x产生了副作用。改变了x的值。 * 引用透明(Referential Transparency)对于相同的输入,总能得到相同的输出。 如果f(x)的参数x和函数体都是引用透明的,那么函数f是纯函数。 违反引用透明

  • 不变性(Immutability)为了获得引用透明性,任何值都不能发生变化。
  • 函数是一等公民(First-class Function)一切都是计算,函数式编程中只有表达式,变量、函数都是表达式。 函数也可以作为参数或者返回值。

  • 表达式求值策略:严格求值和非严格求职。 Call By Value属于严格求值 Call By Name属于非严格求职

  • 惰性求值(Lazy Evaluation)当定义这个变量时不会立即求值,只有在使用到这个变量时才会求值。
  • 递归函数(Recursive Function) 在函数式编程中没有循环语句,所有的循环都要用递归来实现。 递归有个性能问题。容易堆栈溢出。
* 尾递归(Tail Recursion) 函数式编程的优点
  • 生产效率高 同样的功能,Lisp(世界上第一个函数式编程语言)代码长度可能是C代码长度的七到十分之一。
  • 易于推理
  • 并行编程 因为是纯函数,所以不会引起线程紊乱。
  • 多核计算、云计算
搭建scala环境 安装JDK

下载JDK最新版本,将环境变量添加到配置文件中

安装Scala的运行环境和包

从www.scala-lang/download上下载了Scala的最新版本,将环境变量添加到配置文件中就可以用了。

REPL(Read Evaluate Print Loop)就是scala命令行的运行环境。

在linux命令行输入scala就可以直接进入到scala REPL中。

devil@devilshome:~$ scala Welcome to Scala 2.12.0-M4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60). Type in expressions for evaluation. Or try :help. scala> println("Hello World") Hello World scala>

println()函数在所有Scala代码中都可用。

scala> val x =1 x: Int = 1 scala> var x = 1 x: Int = 1 scala>

var 和 val 的区别稍后解释。

使用eclipse开发scala

在scala-ide/download/sdk.html下载专为scala的eclipse - new一个Scala Project - new 一个Scala Worksheet worksheet是可以在IDE中直接进行代码的运行和结果的获得,是交互式的。 默认直接生成一个Object

object worksheet { println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet val x = 1 //> x : Int = 1 println(x) //> 1 }

输入代码以后保存,会直接把输出的结果打印在右边。 新建一个package demo01 在package中新建一个scala app

package demo01 object Greeting extends App { println("Hello,"+args(0)+"!") }

运行时设置main class和参数,main class的格式为包名.类名。 在控制台查看打印输出的结果

Hello,Devil! 使用命令行编译scala

在文件夹中新建一个.scala文件,如Greeting.scala 在命令行中键入

devil@devilshome:~/example$ scalac Greeting.scala

编译完成后,原文夹下多了一个Greeting.class文件,这个就是编译后的字节码文件。 再对这个字节码文件进行使用。

devil@devilshome:~/example$ scala Greeting Jack Hello,Jack! devil@devilshome:~/example$ scala Greeting Devil Hello,Devil!
变量和表达式 变量
  • var 自动生成读写接口,代表的是一个可变的有类型的存储单元。可以在定义变量时为它指定数据,而且可以在任何时间重新赋值。
  • val 只生成了只读接口是一个不可变得、有类型的存储单元。可以在定义值时为它指定数据,不过不允许重新赋值,代表是一种函数式的思维。val蕴含了final的语义。
  • lazy val 定义惰性求值的常量。 可以不显示指定变量的类型,因为Scala会自动进行类型推导。
scala> val x=10 x: Int = 10 scala> val y :Int =20 y: Int = 20 scala> x+x res0: Int = 20 scala> res0 res1: Int = 20 scala> res0*res1 res2: Int = 400 scala> z=200 <console>:16: error: reassignment to val z=200 ^ //val的值不能被修改 scala> scala> val d = 20 d: Int = 20 scala> val e = 30 e: Int = 30 scala> lazy val f = d * e f: Int = <lazy> scala> f * 10 res3: Int = 6000 scala> f res4: Int = 600 scala>

精度问题

scala> val b :Byte = 10 b: Byte = 10 scala> val x :Long = 10 x: Long = 10 scala> val y :Long = b y: Long = 10 scala> val z : Byte = x <console>:12: error: type mismatch; found : Long required: Byte val z : Byte = x ^ scala>
  • 字符类型
scala> val q = 'X' q: Char = X
  • Unit类型 Scala中的Unit类型类似于java中的void。主要的不同是在Scala中可以有一个Unit类型值,也就是(),然而java中是没有void类型的值的。除了这一点,Unit和void是等效的。一般来说每一个返回void的java方法对应一个返回Unit的Scala方法。
scala> val u : Unit=() u: Unit = () scala> val p = () p: Unit = ()
  • Nothing 对于一个函数而言,如果返回值是nothing,则表名该函数发生了异常。
scala> def foo() = throw new Exception("error occurred") foo: ()Nothing
  • String 构建于java的String之上。 新增了字符串插值(interpolation)的特性
scala> val name = "devil" name: String = devil scala> s"my name if ${name}" res5: String = my name if devil
  • Block 在scala中,代码块用于组织多个表达式,block也是一个表达式,其最终的求得的值是最后一个表达式的值。
函数 object func_wxamples { def hello(name: String): String = { s"hello,${name}" } //> hello: (name: String)String hello("devil") //> res0: String = hello,devil def hello2(name: String) = { s"hello,${name}" } //> hello2: (name: String)String hello2("devil") //> res1: String = hello,devil def add (x:Int,y:Int) = x+y //> add: (x: Int, y: Int)Int add(5,3) //> res2: Int = 8 } if表达式

在scala中,if是表达式,而不是语句。

object fun_if_example { if (true) 1 else 2 //> res0: Int = 1 if (false) 3 else 4 //> res1: Int = 4 val a = 1 //> a : Int = 1 if (a == 1) a //> res2: AnyVal = 1 if (a != 1) "不是1" //> res3: Any = () if (a != 1) "不是1" else a //> res4: Any = 1 } for循环 object fun_for_examples { val l = List("elisa", "sin", "satoshi") //> l : List[String] = List(elisa, sin, satoshi) for ( s <- l //用s循环遍历l中的元素 ) println(s) //> elisa //| sin //| satoshi for ( s <- l if (s.length > 3) ) println(s) //> elisa //| satoshi val result_for = for { s <- l s1 = s.toUpperCase() if (s1 != "") } yield (s1) //> result_for : List[String] = List(ELISA, SIN, SATOSHI) }

yield (s1)的意思是将s放入一个新的collection中。

try表达式 try{} catch{} finally{}//无论有没有发生异常,我们都会执行finally这个代码块 match表达式 exp match{ case p1 => val1 case p2 => val2 … case _ => valn } object func_match_example { val code = 3 //> code : Int = 3 val result_match = code match{ case 1 => "one" case 2 => "two" case _ => "three" } //> result_match : String = three } 求值策略

scala里有两种求值策略(Evaluation Strategy) - Call By Value 对函数实参求值,且仅求值一次 - Call By Name 对函数实参每次在函数体内被用到时都会求值。

scala通常使用 Call By Value 如果函数性参类型以 => 开头,那么会使用Call By Name

def foo(x:Int) = x //Call By Value def foo(x:=>Int) = x //Call By Name scala> def bar(x:Int,y: => Int)=1 bar: (x: Int, y: => Int)Int scala> def loop(): Int = loop loop: ()Int scala> bar(1,loop) res6: Int = 1 scala> bar(loop,1)

第二个bar(loop,1)在进行死循环,因为x是Call By Value的需要对所有表达式求值才能进行下一步运算。

高阶函数

scala中函数是第一等公民 Scala语言支持: 1. 把函数作为实参传递给另外一个函数 2. 把函数作为返回值 3. 把函数赋值给变量 4. 把函数存储在数据结构中 在scala中,函数就像普通变量一样,同样也具有函数类型。

  • 在scala中,函数类型的格式为 A=>B,表示一个接收类型A的参数,并返回类型B的函数。

高阶函数 用函数作为形参或返回值的函数,成为高阶函数


匿名函数 就是函数常量,也成为函数文字量。 在scala里,匿名函数的定一个是为(形参列表)=>{函数体}

柯里化

柯里化函数把具有多个参数的函数转换为一条函数链,每个节点上是单一参数。

eg:下面两个函数的定义是等价的

def add(x: Int,y: Int) = x+y def add(x: Int)(y: Int) = x+y 递归函数

递归函数在函数式变成中是实现循环的一种技术。

def factorial(n: Int): Int = if (n<=0) 1 else n* factorial(n-1)

尾递归 尾递归函数中所有递归形式的调用都出现在函数的末尾。 当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。 避免堆栈溢出

@annotation.tailrec //告诉scala编译器,对这个递归进行尾递归优化 def factorial(n: Int,m: Int): Int = if (n<=0) m else factorial(n-1,m*n) factorial(5,1)

在上面这个案例增加了一个m,m其实是保留了n*n-1*n-2……这样一个累乘器。 m永远拿到都是过去几次累乘的结果。

本文标签: 入门语言Scala