Scala编程基础

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala编程基础相关的知识,希望对你有一定的参考价值。

ScalaJava的关系... 1

安装Scala. 1

Scala解释器的使用... 2

声明变量... 2

数据类型与操作符... 2

函数调用与apply()函数... 3

if表达式... 3

语句终结符、块表达式... 4

输入和输出... 4

循环... 4

高级for循环... 5

函数的定义与调用... 5

在代码块中定义包含多行语句的函数体... 5

递归函数与返回类型... 6

默认参数... 6

函数调用时带名参数... 6

变长参数... 6

序列作为变长参数... 6

过程... 7

lazy... 7

异常... 7

Array. 8

ArrayBuffer. 8

遍历ArrayArrayBuffer. 8

数组常见操作... 9

 

ScalaJava的关系

ScalaJava的关系是非常紧密的!!

因为Scala是基于Java虚拟机,也就是JVM的一门编程语言。所有Scala的代码,都需要经过编译为字节码,然后交由Java虚拟机来运行。

所以ScalaJava是可以无缝互操作的。Scala可以任意调用Java的代码。所以ScalaJava的关系是非常非常紧密的。

安装Scala

·Scala官方网站下载,http://www.scala-lang.org/download/windows版本的安装包是scala-2.11.7.msi

·使用下载下来的安装包安装Scala

·PATH环境变量中,配置$SCALA_HOME/bin目录。

·windows命令行内即可直接键入scala,打开scala命令行,进行scala编程。

Scala解释器的使用

·REPLRead(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环)。scala解释器也被称为REPL,会快速编译scala代码为字节码,然后交给JVM来执行。

·计算表达式:在scala>命令行内,键入scala代码,解释器会直接返回结果给你。如果你没有指定变量来存放这个值,那么值默认的名称为res,而且会显示结果的数据类型,比如IntDoubleString等等。

·例如,输入1 + 1,会看到res0: Int = 2

·内置变量:在后面可以继续使用res这个变量,以及它存放的值。

·例如,2.0 * res0,返回res1: Double = 4.0

·例如,"Hi, " + res0,返回res2: String = Hi, 2

·自动补全:scala>命令行内,可以使用Tab键进行自动补全。

·例如,输入res2.to,敲击Tab键,解释器会显示出以下选项,toCharArraytoLowerCasetoStringtoUpperCase。因为此时无法判定你需要补全的是哪一个,因此会提供给你所有的选项。

·例如,输入res2.toU,敲击Tab键,直接会给你补全为res2.toUpperCase

声明变量

·声明val变量:可以声明val变量来存放表达式的计算结果。

·例如,val result = 1 + 1

·后续这些常量是可以继续使用的,例如,2 * result

·但是常量声明后,是无法改变它的值的,例如,result = 1,会返回error: reassignment to val的错误信息。

·声明var变量:如果要声明值可以改变的引用,可以使用var变量。

·例如,val myresult = 1myresult = 2

·但是在scala程序中,通常建议使用val,也就是常量,因此比如类似于spark的大型复杂系统中,需要大量的网络传输数据,如果使用var,可能会担心值被错误的更改。

·Java的大型复杂系统的设计和开发中,也使用了类似的特性,我们通常会将传递给其他模块 / 组件 / 服务的对象,设计成不可变类(Immutable Class)。在里面也会使用java的常量定义,比如final,阻止变量的值被改变。从而提高系统的健壮性(robust,鲁棒性),和安全性。

·指定类型:无论声明val变量,还是声明var变量,都可以手动指定其类型,如果不指定的话,scala会自动根据值,进行类型的推断。

·例如,val name: String = null

·例如,val name: Any = "leo"

·声明多个变量:可以将多个变量放在一起进行声明。

·例如,val name1, name2:String = null

·例如,val num1, num2 = 100

数据类型与操作符

·基本数据类型:ByteCharShortIntLongFloatDoubleBoolean

·乍一看与Java的基本数据类型的包装类型相同,但是scala没有基本数据类型与包装类型的概念,统一都是类。scala自己会负责基本数据类型和引用类型的转换操作。

·使用以上类型,直接就可以调用大量的函数,例如,1.toString()1.to(10)

·类型的加强版类型scala使用很多加强类给数据类型增加了上百种增强的功能或函数。

·例如,String类通过StringOps类增强了大量的函数,"Hello".intersect(" World")

·例如,Scala还提供了RichIntRichDoubleRichChar等类型,RichInt就提供了to函数,1.to(10),此处Int先隐式转换为RichInt,然后再调用其to函数

·基本操作符:scala的算术操作符与java的算术操作符也没有什么区别,比如+-*/%等,以及&|^>><<等。

·但是,在scala中,这些操作符其实是数据类型的函数,比如1 + 1,可以写做1.+(1)

·例如,1.to(10),又可以写做1 to 10

·scala中没有提供++--操作符,我们只能使用+-,比如counter = 1counter++是错误的,必须写做counter += 1.

函数调用与apply()函数

·函数调用方式:scala中,函数调用也很简单。

·例如,import scala.math._sqrt(2)pow(2, 4)min(3, Pi)

·不同的一点是,如果调用函数时,不需要传递参数,则scala允许调用函数时省略括号的,例如,"Hello World".distinct

·apply函数

    ·Scala中的apply函数是非常特殊的一种函数,在Scalaobject中,可以声明apply函数。而使用类名()”(严格来讲应该是对象名()”)的形式,其实就是类名.apply()”(严格来讲应该是对象名.apply()”)的一种缩写。通常使用这种方式来构造类的对象,而不是使用“new 类名()”的方式。

·例如,"Hello World"(6),因为在StringOps类中有def apply(n: Int): Char的函数定义,所以"Hello World"(6),实际上是"Hello World".apply(6)的缩写。

·例如,Array(1, 2, 3, 4),实际上是用Array objectapply()函数来创建Array类的实例,也就是一个数组。

 

if表达式

·if表达式的定义:Scala中,if表达式是有值的,就是if或者else中最后一行语句返回的值。

    ·例如,val age = 30; if (age > 18) 1 else 0

    ·可以将if表达式赋予一个变量,例如,val isAdult = if (age > 18) 1 else 0

    ·另外一种写法,var isAdult = -1; if(age > 18) isAdult = 1 else isAdult = 0,但是通常使用上一种写法

·if表达式的类型推断:由于if表达式是有值的,而ifelse子句的值类型可能不同,此时if表达式的值是什么类型呢?Scala会自动进行推断,取两个类型的公共父类型。

    ·例如,if(age > 18) 1 else 0,表达式的类型是Int,因为10都是Int

    ·例如,if(age > 18) "adult" else 0,此时ifelse的值分别是StringInt,则表达式的值是AnyAnyStringInt的公共父类型

    ·如果if后面没有跟else,则默认else的值是Unit,也用()表示,类似于java中的void或者null。例如,val age = 12; if(age > 18) "adult"。此时就相当于if(age > 18) "adult" else ()

·if语句放在多行中:默认情况下,REPL只能解释一行语句,但是if表达式通常需要放在多行。

    ·可以使用{}的方式,比如以下方式,或者使用:pastectrl+D的方式。

if(age > 18) { "adult"

} else if(age > 12) "teenager" else "children"

语句终结符、块表达式

·默认情况下,scala不需要语句终结符,默认将每一行作为一个语句

·一行放多条语句:如果一行要放多条语句,则必须使用语句终结符

    ·例如,使用分号作为语句终结符,var a, b, c = 0; if(a < 10) { b = b + 1; c = c + 1 }

    ·通常来说,对于多行语句,还是会使用花括号的方式

if(a < 10) {

    b = b + 1

    c = c + 1

}

·块表达式:块表达式,指的就是{}中的值,其中可以包含多条语句,最后一个语句的值就是块表达式的返回值。

    ·例如,var d = if(a < 10) { b = b + 1; c + 1 }

输入和输出

·printprintlnprint打印时不会加换行符,而println打印时会加一个换行符。

    ·例如,print("Hello World"); println("Hello World")

·printfprintf可以用于进行格式化

    ·例如,printf("Hi, my name is %s, I‘m %d years old.\n", "Leo", 30)

·readLine: readLine允许我们从控制台读取用户输入的数据,类似于java中的System.inScanner的作用。

·综合案例:游戏厅门禁

val name = readLine("Welcome to Game House. Please tell me your name: ")

print("Thanks. Then please tell me your age: ")

val age = readInt()

if(age > 18) {

  printf("Hi, %s, you are %d years old, so you are legel to come here!", name, age)

} else {

  printf("Sorry, boy, %s, you are only %d years old. you are illegal to come here!", name, age)

}

循环

·while do循环:Scalawhile do循环,基本语义与Java相同。

var n = 10

while(n > 0) {

  println(n)

  n -= 1

}

·Scala没有for循环,只能使用while替代for循环,或者使用简易版的for语句

    ·简易版for语句:var n = 10; for(i <- 1 to n) println(i)

    ·或者使用until,表式不达到上限:for(i <- 1 until n) println(i)

    ·也可以对字符串进行遍历,类似于java的增强for循环,for(c <- "Hello World") print(c)

·跳出循环语句

    ·scala没有提供类似于javabreak语句。

    ·但是可以使用boolean类型变量、return或者Breaksbreak函数来替代使用。

import scala.util.control.Breaks._

breakable {

    var n = 10

    for(c <- "Hello World") {

        if(n == 5) break;

        print(c)

        n -= 1

    }

}

高级for循环

·多重for循环:九九乘法表

for(i <- 1 to 9; j <- 1 to 9) {

  if(j == 9) {

    println(i * j)

  } else {

    print(i * j + " ")

  }

}

·if守卫:取偶数

for(i <- 1 to 100 if i % 2 == 0) println(i)

·for推导式:构造集合

for(i <- 1 to 10) yield i

 

函数的定义与调用

Scala中定义函数时,需要定义函数的函数名、参数、函数体。

我们的第一个函数如下所示:

def sayHello(name: String, age: Int) = {

  if (age > 18) { printf("hi %s, you are a big boy\n", name); age }

  else { printf("hi %s, you are a little boy\n", name); age

}

sayHello("leo", 30)

Scala要求必须给出所有参数的类型,但是不一定给出函数返回值的类型,只要右侧的函数体中不包含递归的语句,Scala就可以自己根据右侧的表达式推断出返回类型。

在代码块中定义包含多行语句的函数体

单行的函数:def sayHello(name: String) = print("Hello, " + name)

如果函数体中有多行代码,则可以使用代码块的方式包裹多行代码,代码块中最后一行的返回值就是整个函数的返回值。与Java中不同,不是使用return返回值的。

比如如下的函数,实现累加的功能:

def sum(n: Int) = {

  var sum = 0;

  for(i <- 1 to n) sum += i

  sum

}

递归函数与返回类型

如果在函数体内递归调用函数自身,则必须手动给出函数的返回类型。

例如,实现经典的斐波那契数列:

9 + 8; 8 + 7 + 7 + 6; 7 + 6 + 6 + 5 + 6 + 5 + 5 + 4; ....

def fab(n: Int): Int = {

  if(n <= 1) 1

  else fab(n - 1) + fab(n - 2)

}

默认参数

Scala中,有时我们调用某些函数时,不希望给出参数的具体值,而希望使用参数自身默认的值,此时就定义在定义函数时使用默认参数。

def sayHello(firstName: String, middleName: String = "William", lastName: String = "Croft") = firstName + " " + middleName + " " + lastName

如果给出的参数不够,则会从左往右依次应用默认参数。

def sayHello(name: String, age: Int = 20) {

  print("Hello, " + name + ", your age is " + age)

}

sayHello("leo")

函数调用时带名参数

在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。

sayHello(firstName = "Mick", lastName = "Nina", middleName = "Jack")

还可以混合使用未命名参数和带名参数,但是未命名参数必须排在带名参数前面。

sayHello("Mick", lastName = "Nina", middleName = "Jack")

变长参数

Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数。

def sum(nums: Int*) = {

  var res = 0

  for (num <- nums) res += num

  res

}

sum(1, 2, 3, 4, 5)

序列作为变长参数

在如果想要将一个已有的序列直接调用变长参数函数,是不对的。比如val s = sum(1 to 5)。此时需要使用Scala特殊的语法将参数定义为序列,让Scala解释器能够识别。这种语法非常有用!一定要好好主意,在spark的源码中大量地使用到了。

val s = sum(1 to 5: _*)

案例:使用递归函数实现累加

def sum2(nums: Int*): Int = {

  if (nums.length == 0) 0

  else nums.head + sum2(nums.tail: _*)

}

过程

Scala中,定义函数时,如果函数体直接包裹在了花括号里面,而没有使用=连接,则函数的返回值类型就是Unit。这样的函数就被称之为过程,即过程就是没有返回值的函数。

过程还有一种写法,就是将函数的返回值类型定义为Unit

def sayHello(name: String) = "Hello, " + name//函数

def sayHello(name: String) { print("Hello, " + name); "Hello, " + name }//有值,但未使用=号,还是过程

def sayHello(name: String): Unit = "Hello, " + name//有值,有=号,但强制返回类型为空,则还是过程

lazy

Scala中,提供了lazy值的特性,也就是说,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算。这种特性对于特别耗时的计算操作特别有用,比如打开文件进行IO,进行网络IO等。

 

import scala.io.Source._

lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

即使文件不存在,也不会报错,只有第一个使用变量时会报错,证明了表达式计算的lazy特性。

 

val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

def lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

异常

Scala中,异常处理和捕获机制与Java是非常相似的。

try {

  throw new IllegalArgumentException("x should not be negative")

} catch {

  case _: IllegalArgumentException => println("Illegal Argument!")

} finally {

  print("release resources!")

}

 

Import java.io._

try {

  throw new IOException(“io exception!!!")

} catch {

  case _: IllegalArgumentException => println("illegal argument")

}

 

try {

  throw new IOException("user defined exception")

} catch {

  case e1: IllegalArgumentException => println("illegal argument")

  case e2: IOException => println("io exception")

}

Array

Scala中,Array代表的含义与Java中类似,也是长度不可改变的数组。此外,由于ScalaJava都是运行在JVM中,双方可以互相调用,因此Scala数组的底层实际上是Java数组。例如字符串数组在底层就是JavaString[],整数数组在底层就是JavaInt[]

// 数组初始化后,长度就固定下来了,而且元素全部根据其类型初始化

val a = new Array[Int](10)

a(0)

a(0) = 1

val a = new Array[String](10)

// 可以直接使用Array()创建数组,元素类型自动推断

val a = Array("hello", "world")

a(0) = "hi"

val a = Array("leo", 30)

ArrayBuffer

Scala中,如果需要类似于Java中的ArrayList这种长度可变的集合类,则可以使用ArrayBuffer

// 如果不想每次都使用全限定名,则可以预先导入ArrayBuffer

import scala.collection.mutable.ArrayBuffer

// 使用ArrayBuffer()的方式可以创建一个空的ArrayBuffer

val b = ArrayBuffer[Int]()

// 使用+=操作符,可以添加一个元素,或者多个元素

// 这个语法必须要谨记在心!因为spark源码里大量使用了这种集合操作语法!

b += 1

b += (2, 3, 4, 5)

// 使用++=操作符,可以添加其他集合中的所有元素

b ++= Array(6, 7, 8, 9, 10)

// 使用trimEnd()函数,可以从尾部截断指定个数的元素

b.trimEnd(5)

 

// 使用insert()函数可以在指定位置插入元素

// 但是这种操作效率很低,因为需要移动指定位置后的所有元素

b.insert(5, 6)

b.insert(6, 7, 8, 9, 10)

// 使用remove()函数可以移除指定位置的元素

b.remove(1)

b.remove(1, 3)

// ArrayArrayBuffer可以互相进行转换

b.toArray

a.toBuffer

遍历ArrayArrayBuffer

// 使用for循环和until遍历Array / ArrayBuffer

// 使untilRichInt提供的函数

for (i <- 0 until b.length)

  println(b(i))

// 跳跃遍历Array / ArrayBuffer

for(i <- 0 until (b.length, 2))

  println(b(i))

// 从尾部遍历Array / ArrayBuffer

for(i <- (0 until b.length).reverse)

  println(b(i))

// 使用增强for循环遍历Array / ArrayBuffer

for (e <- b)

  println(e)

数组常见操作

// 数组元素求和

val a = Array(1, 2, 3, 4, 5)

val sum = a.sum

// 获取数组最大值

val max = a.max

// 对数组进行排序

scala.util.Sorting.quickSort(a)

// 获取数组中所有元素内容

a.mkString

a.mkString(", ")

a.mkString("<", ",", ">")

// toString函数

a.toString

b.toString

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

附件列表

     

    以上是关于Scala编程基础的主要内容,如果未能解决你的问题,请参考以下文章

    Scala学习笔记编程基础

    Scala 中的函数式编程基础

    Scala基础 模式匹配样例类与Actor编程

    Scala基础 模式匹配样例类与Actor编程

    Scala基础

    Scala基础高阶函数隐式转换AKKA编程

    (c)2006-2024 SYSTEM All Rights Reserved IT常识