Scala学习笔记-基本语法-实践tricks

写在前面:

    多学、多记、多写代码,最后做一个优秀码农,因为每一个码农都是一个资深美食家,哈哈 ~~~

    我是一个matlab出家的码农,java不会,c++不熟,c皮毛,python基本不会,为了做数据分析,用spark开发,开始学习scala,本blog记录我的学习过程,不知两年后能否写出优秀的代码。

部分简称的全称:

FP:Functional Programming 函数式程序设计

OO:Object Oriented 面向对象的

1.scala的优点和简介

Scala虽然是一门彻头彻底的静态语言,但又具备了现代动态语言的很多方便和灵活:
一、不需要冗余的类型声明

二、map替代各种for循环,简化代码。

快捷键使用(mac):

command+空格:能看一个函数的原代码;

option+enter:将鼠标放在一个变量或者常量A上,能够看到这个A的类型。

2.基本语法部分

2.0 scala简单语法

(假装你已经安装好scala,打开终端,输入 $:scala 回车,进入scala编译环境)

scala> List(1,31,4,53,4,3,234)

res0: List[Int] = List(1, 31, 4, 53, 4, 3, 234)

第一行代码,怎么少的了hello world!

scala> println("Hello World!")

Hello World!

语句尾部分号问题:scala中一行代码结束,可以不写分号";",如果一行写多个语句,需要在句末尾加上分号。

一行语句不能写两行,scala默认将一行代码作为一个语句,除非遇见语句分隔号——分号。

举个例子:将1+2分为两行写,就是两个语句,输出两结果:

scala> 1

res3: Int = 1

scala> +2

res4: Int = 2

scala> 1+2

res5: Int = 3

这种情况解决办法:

一、用括号包含分行代码

scala> (1

     | +2)

res6: Int = 3

2.1 scala基本类型

类型(第一个单词是大写) 范围
Byte 8bit
Short 16bit
Int 32bit
Long 64bit
Char 16bit
String a sequence of chars
Float 32bit
Double 64bit
Booleam true of false

2.2变量定义

一、val:不可变变量,又名常量。建议如果这个变量的数值,在后续使用中不变,那么定义的时候定义为val,便于追踪bug。

scala> val temp =1

temp: Int = 1

scala> temp =2

<console>:12: error: reassignment to val

       temp =2

            ^

二、var:可变变量。后续可以改变这个变量的值。

scala> var temp = 1

temp: Int = 1

scala> temp =2

temp: Int = 2

2.3函数定义


可以简写为, 返回值类型不需要写, 可以推断出, 只有一条语句, 所以{}可以省略:

scala> def max2(x:Int,y:Int)=if(x>y) x else y

max2: (x: Int, y: Int)Int

简单的funciton, 返回值为Unit, 类似Void(区别在于void为无返回值, 而scala都有返回值, 只是返回的为Unit,()):

scala> def goodluck()=println("hello world!")

goodluck: ()Unit

那么我们尝试判断一下goodluck的类型是不是unit呢,unit这里就是():看到下面的代码,肯定了goodluck的返回值是()类型。

scala> goodluck() == ()

hello world!

res8: Boolean = true

2.4函数形式(function literal)

cala FP的基础, function作为first class, 以function literal的形式作为参数被传递


scala> List(1,2,3,4)
res9: List[Int] = List(1, 2, 3, 4)
scala> res9.foreach((arg:Int)=>println(arg))
1
2
3
4
scala> res9.foreach(arg=>println(arg))   //省略类型
1
2
3
4
scala> res9.foreach(println)   //其实连参赛列表也可以省略
1
2
3
4
可以看到scala在省略代码量上可以说下足功夫, 只要能推断出来的你都可以不写, 这也是对于静态类型系统的一种形式的弥补
对于oo程序员, 可能比较难理解, 其实等于:
for (arg <-args)
  println(arg)

2.5控制结构

由于scala是偏向于FP的, 所以所有控制结构都有返回值, 这样便于FP编程。

If, 可以返回值
val filename =  
  if (!args.isEmpty) args(0)  
  else "default.txt"
While, 在FP里面不推荐使用循环, 应该用递归,尽量避免
For, 没有python和clojure的好用或简洁
for (
  file <- filesHere //generator,用于遍历,每次file都会被从新初始化
  if file.isFile;  //过滤条件, 多个条件需要用;竟然没有and操作,,这个后续再检查更新,这个不是很确定。
  if file.getName.endsWith(".scala"); //第二个过滤
  line <- fileLines(file)  //嵌套for
  trimmed = line.trim  //Mid-stream variable bindings, val类型,类似clojure let
  if trimmed.matches(pattern)
) println(file +": "+ trimmed)
//for默认不会产生新的集合, 必须使用yield
def scalaFiles =
  for {
    file <- filesHere
    if file.getName.endsWith(".scala")
  } yield file //yield产生新的集合,类似python

2.5数据结构

2.5.1数组

可变的同类对象序列, 适用于OO场景。

 
 

scala> val g = new Array[String](3)

g: Array[String] = Array(null, null, null)

scala> g(0)="hello"

scala> g(1)=","

scala> g(2)="world!"

scala> g.foreach(println)

hello

,

world!

Scala 操作符等价于方法, 所以任意方法都可以以操作符的形式使用1 + 2 //(1).+(2), 在只有一个参数的情况下, 可以省略.和()0 to 2 //(0).to(2)greetStrings(0) //greetStrings.apply(0),这也是为什么scala使用(), 而非[]

greetStrings(0) = "Hello" //greetStrings.update(0, "Hello")

简化的array初始化:

scala> val num=Array("one","two","threee")
num: Array[String] = Array(one, two, threee)
scala> val num =Array.apply("one","two","three")
num: Array[String] = Array(one, two, three)

2.5.2List

相对于array, List为不可变对象序列, 适用于FP场景.

对于List最常用的操作符为:

::——cons, 把新的elem放到list最前端 

:::——两个list的合并
scala> val oneTwo = List(1, 2)
oneTwo: List[Int] = List(1, 2)

scala> val threeFour = List(3, 4)
threeFour: List[Int] = List(3, 4)

scala> val zeroOneTwo = 0 :: oneTwo
zeroOneTwo: List[Int] = List(0, 1, 2)

scala> val oneTwoThreeFour = oneTwo ::: threeFour
oneTwoThreeFour: List[Int] = List(1, 2, 3, 4)
一、右操作数, ::
普通情况下, 都是左操作数, 比如, a * b  => a.*(b) 
但是当方法名为:结尾时, 为右操作数 

1 :: twoThree  => twoThree.::(1)

二、不支持append 
原因是, 这个操作的耗时会随着list的长度变长而线性增长, 所以不支持, 只支持前端cons, 实在需要append可以考虑ListBuffer。

三、方法:拉链操作zip

scala> List(1,2,3,4)
res20: List[Int] = List(1, 2, 3, 4)
scala> List("a","b","c","d")
res22: List[String] = List(a, b, c, d)
scala> res20.zip(res22)
res26: List[(Int, String)] = List((1,a), (2,b), (3,c), (4,d))

四、方法:drop(n)

返回去掉前n个元素的列表

scala> res20.drop(3)
res28: List[Int] = List(4)

五、方法:exists()

判断列表中是否包含某个元素。返回值类型为布尔类型。

scala> res20.exists(s=>s==3)
res29: Boolean = true

scala> res20.exists(s=>s==5)
res30: Boolean = false

六、方法filter()

过滤作用,根据需求写语句。

七、map()

对列表中的每一个元素都进行同样的操作。

scala> res20.map(s => s*2)
res34: List[Int] = List(2, 4, 6, 8)

2.5.3 Queues 队列

两种形式:可变队列和不可变队列。

import scala.collection.immutable.Queue //不可变Queue
val empty = new Queue[Int]
val has1 = empty.enqueue(1) //添加单个元素
val has123 = has1.enqueue(List(2, 3)) //添加多个元素
val (element, has23) = has123.dequeue //取出头元素,返回两个值, 头元素和剩下的queue
element: Int = 1
has23: scala.collection.immutable.Queue[Int] = Queue(2,3)
 
import scala.collection.mutable.Queue //可变Queue
val queue = new Queue[String]
queue += "a"  //添加单个
queue ++= List("b", "c") //添加多个
queue.dequeue //取出头元素, 只返回一个值
res22: String = a
scala> queue
res23: scala.collection.mutable.Queue[String] = Queue(b, c)

2.5.4 Stack 堆

import scala.collection.mutable.Stack
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
scala> stack.top
res8: Int = 2
scala> stack.pop
res10: Int = 2
scala> stack
res11: scala.collection.mutable.Stack[Int] = Stack(1)

2.5.5 Tuple 元组

tuple和list一样是不可变的, 不同是, list中的elem必须是同一种类型, 但tuple中可以包含不同类型的elem。

scala> val pair =(1,"pearl") //自动推断出类型为,Tuple2[Int, String]
pair: (Int, String) = (1,pearl)

scala> println(pair._1)  //从1开始,而不是0,依照Haskell and ML的传统
1

scala> println(pair._2)  //elem访问方式不同于list, 由于元组中elem类型不同
pearl

2.5.6 set 和map

scala> var jetSet = Set("Boeing", "Airbus")
jetSet: scala.collection.immutable.Set[String] = Set(Boeing, Airbus)

scala> jetSet += "Lear"

scala> println(jetSet.contains("Cessna"))
false

2.6 面向对象-OO

2.6.1类和对象

相对于Java定义比较简单, 默认public
class ChecksumAccumulator {
  private var sum = 0
  def add(b: Byte): Unit = {
    sum += b
  }  
  def checksum(): Int = {
    return ~(sum & 0xFF) + 1
  }
}

进一步简化, 去掉{ }和return, 默认将最后一次计算的值返回,还有赋值不能作为返回值。想返回哪个参数,可以在最后一句写上该变量的名字。
不写return是推荐的方式, 因为函数尽量不要有多个出口, 
class ChecksumAccumulator {
  private var sum = 0
  def add(b: Byte): Unit = sum += b
  def checksum(): Int = ~(sum & 0xFF) + 1
}

其实对于Unit(void), 即没有返回值, 对于FP而言, 就是该function只会产生side effect, 还有另外一种简写的方式 
class ChecksumAccumulator {
  private var sum = 0
  def add(b: Byte) { sum += b } //对于Unit返回的, 另一种简写, 用{}来表示无返回, 所以前面的就不用写了
  def checksum(): Int = ~(sum & 0xFF) + 1
}

实例化:
val acc = new ChecksumAccumulator
val csa = new ChecksumAccumulator

acc.sum = 3

初始化的时候,各个小函数都是0,当赋值或者操作的时候才会改变对应小函数值。


可变对象
可变Objects本身没啥好说的, 说说Scala getter和setter规则
every var that is a non-private member of some object implicitly defines a getter and a setter method with it. 
The getter of a var x is just named “x”, while its setter is named “x_=”.
每个非私有的var都会隐含的自动定义getter和setter, 如下面的例子
class Time {
    var hour = 12
    var minute = 0
}
//等同于
class Time {
    private[this] var h = 12
    private[this] var m = 0
    def hour: Int = h
    def hour_=(x: Int) { h = x }
    def minute: Int = m
    def minute_=(x: Int) { m = x }
}
所以在Scala中比较有趣的是, 其实你可以不真正定义这个成员, 而只需要定义getter和setter就可以 
如下面的例子, 并没有真正定义华氏温度, 而只是定义了getter和setter, 更简洁
class Thermometer {
    var celsius: Float = _
    def fahrenheit = celsius * 9 / 5 + 32
    def fahrenheit_= (f: Float) {
        celsius = (f 32)
        * 5 / 9
    }
    override def toString = fahrenheit +"F/"+ celsius +"C"
}




优秀笔记:

http://qiujj.com/static/Scala-Handbook.htm (这个写的有点高深了)

http://www.cnblogs.com/fxjwind/p/3338829.html (这个我个人认为还较为基础)

猜你喜欢

转载自blog.csdn.net/pearl8899/article/details/80555543