文章目录
1、Windows下安装Scala
Scala是一种类似Java的纯面向对象的函数式编程语言,由于函数具有明确的确定输入对确定输出的关系,所以适合推理和计算,一切函数都可以看成一系列的计算组成,另外由于Scala函数是没有副作用和透明的,所以非常适合做多核并行计算和云计算,现在正被越来越多的应用到生产中
因为Scala运行在JVM上,所以应该首先配置好JDK环境(>1.5版本),然后下面是Windows安装Scala环境的过程:
首先去官网http://www.scala-lang.org/下载相应的Windows安装包
然后就是双击一直下一步下一步到Finish
安装完毕即可。
安装Scala之后,需要配置一下环境变量,从而直接从命令行访问,配置过程和JDK类似,进入Windows环境变量,此处不再详细描述,然后在系统变量中新增SCALA_HOME
变量:
然后单击确定,编辑Path变量,在后面追加如下配置:
然后单击确定,最后再次确定保存环境变量设置,到这里Scala配置完毕,然后检验配置是否成功,运行cmd,输入 scala -version 可以正常输出版本号:
2、数据类型
Scala 与 Java有着相同的数据类型,下表列出了 Scala 支持的数据类型:
数据类型 | 描述 |
---|---|
Byte | 8位有符号补码整数。数值区间为 -128 到 127 |
Short | 16位有符号补码整数。数值区间为 -32768 到 32767 |
Int | 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long | 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 |
Float | 32 位, IEEE 754 标准的单精度浮点数 |
Double | 64 位 IEEE 754 标准的双精度浮点数 |
Char | 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF |
String | 字符序列 |
Boolean | true或false |
Unit | 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 |
Null | null 或空引用 |
Nothing | Nothing类型在Scala的类层级的最底端;它是任何其他类型的子类型。 |
Any | Any是所有其他类的超类 |
AnyRef | AnyRef类是Scala里所有引用类(reference class)的基类 |
上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。 |
在 Scala 中,使用关键词 “var” 声明变量,使用关键词 “val” 声明常量。声明变量实例如下:
var myVar : String = "Foo"
var myVar : String = "Too"
以上定义了变量 myVar,我们可以修改它。声明常量实例如下:
val myVal : String = "Foo"
以上定义了常量 myVal,它是不能修改的。如果程序尝试修改常量 myVal 的值,程序将会在编译时报错。
Scala 中使用 val 语句可以定义函数,def 语句定义方法:
class Test{
def m(x: Int) = x + 3
val f = (x: Int) => x + 3
}
参考:Scala 教程
3、字符串前面的s和f
字符串中的变量替换,Scala中基础的字符串插值就是在字符串前加字幕s
,然后在字符串中放入变量,每个变量都应以$
开头。字符串前加字母s
时,其实是在创建一个处理字符串字面量:
object Demo01 {
def main(args:Array[String])={
var name = "zhangsan"
var age = 15
println(s"name=$name,age=$age")
}
}
// 结果:
// name=zhangsan,age=15
在字符串字面量中使用表达式,${}
内可嵌入任何表达式,包括等号表达式:
def main(args:Array[String])={
var name = "zhangsan"
var age = 15
println(s"name=$name,age=${age+1}")
}
// 结果:
// name=zhangsan,age=16
printf格式化:
def main(args:Array[String])={
var name = "zhangsan"
var age = 15
var score = 89.5f
printf(f"name=$name,age=${age+1},score=$score%.2f")
}
// 结果:
// name=zhangsan,age=16,score=89.50
4、for循环
Scala 语言中 for 循环的语法:
for( var x <- Range ){
statement(s);
}
以上语法中,Range 可以是一个数字区间表示 i to j
,或者 i until j
。左箭头 <-
用于为变量 x 赋值。
以下是一个使用了 i to j
语法的实例:
object Test {
def main(args: Array[String]) {
var a = 0;
// for 循环
for( a <- 1 to 5){ // 或者for( a <- 1 until 10){
println( "Value of a: " + a );
}
}
}
// 结果:
$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 3
value of a: 4
value of a: 5
在 for 循环 中你可以使用分号 ;
来设置多个区间,它将迭代给定区间所有的可能值。以下实例演示了两个区间的循环实例:
def main(args: Array[String]) {
var a = 0;
var b = 0;
// for 循环
for( a <- 1 to 3; b <- 1 to 3){
println( "Value of a: " + a );
println( "Value of b: " + b );
}
}
for 循环集合的实例如下:
def main(args: Array[String]) {
var a = 0;
val numList = List(1,2,3,4,5,6);
// for 循环
for( a <- numList ){
println( "Value of a: " + a );
}
}
Scala 可以使用一个或多个 if 语句来过滤一些元素。实例:
def main(args: Array[String]) {
var a = 0;
val numList = List(1,2,3,4,5,6,7,8,9,10);
// for 循环
for( a <- numList
if a != 3; if a < 8 ){
println( "Value of a: " + a );
}
}
// 结果:
value of a: 1
value of a: 2
value of a: 4
value of a: 5
value of a: 6
value of a: 7
你可以将 for 循环的返回值作为一个变量存储。语法格式如下:
var retVal = for{ var x <- List
if condition1; if condition2...
}yield x
注意:大括号中用于保存变量和条件,retVal
是变量, 循环中的 yield
会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。
def main(args: Array[String]) {
var a = 0;
val numList = List(1,2,3,4,5,6,7,8,9,10);
// for 循环
var retVal = for{ a <- numList
if a != 3; if a < 8
}yield a
// 输出返回值
for( a <- retVal){
println( "Value of a: " + a );
}
}
// 结果:
value of a: 1
value of a: 2
value of a: 4
value of a: 5
value of a: 6
value of a: 7
5、Scala中的 -> 与 <- 以及 =>
<-
只会出现在for循环里面;->
只会出现在k->v
里面;但是=>
就较为复杂了,有如下几种用法:
(1)表示函数的返回类型(Function Type):
//定义函数
scala> def example(x: Int): Int = x*2
example: (x: Int)Int
//定义一个函数变量:
scala> var x : (Int) => Int = example
x: Int => Int = <function1>
//调用
scala> x(2)
res1: Int = 4
函数example的类型就是 (x: Int) => Int
或者 Int => Int
。左边是参数类型,右边是方法返回值类型。备注: 当函数只有一个参数的时候,函数类型里面括起来函数参数的括号是可以省略的。
(2)匿名函数:
//通过匿名函数定义一个函数变量xx
scala> var xx = (x: Int) => x + 1
xx: Int => Int = <function1>
//给一个高阶函数,传递一个函数:
scala> val newList = List(1,2,3).map { (x: Int) => x * 2 }
newList: List[Int] = List(2, 4, 6)
匿名函数定义,=>
左边是参数 右边是函数实现体 (x: Int)=>{}
无参匿名函数:() => T
相当于new Function0[T] //T是返回类型
,下面是示例代码:
scala> val f: () => Unit = () => { println("x")}
f: () => Unit = <function0>
scala> f()
x
scala> val f2: Function0[Unit] = () => println("x2")
f: () => Unit = <function0>
scala> f2()
x2
(3)case语句:
scala> val x = 10; val y = 20
x: Int = 10
y: Int = 20
scala> val max = x > y match {
case true => x
case false => y
}
max: Int = 20
在模式匹配 match
和 try-catch
都用 =>
表示输出的结果或返回的值
(4)By-Name Parameters(传名参数):
在函数执行时才对参数求值:
//函数doubles
scala> def doubles(x: => Int) = {
println("Now doubling " + x)
x*2
}
doubles: (x: => Int)Int
//调用函数
scala> doubles(3)
Now doubling 3
res2: Int = 6
scala> def f(x: Int): Int = {
println(s"Calling f($x)")
x+1
}
f: (x: Int)Int
//调用函数
scala> doubles(f(3))
Calling f(3)
Now doubling 4
Calling f(3)
res9: Int = 8
总觉得这个例子不咋对,参考自:scala中“=>”的4种使用场景,下面这个好像才对,参考自:Scala => 用法总结
def f(x: => Int) = x * x
// call it now
var y = 0
f { y += 1; y }
// result like eval { y += 1; y } * { y += 1; y }
2
注:{y+=1;y} 并不特别,只是对y做了加1的操作,后面的;y没有起到任何修改y值的作用。 1 * 2 = 2。 就这么计算出来的。
(5)map函数中使用:
这个十分常用,尤其在Spark里对RDD等数据类型操作时。这里很像匿名函数的创建方式,其实是lambda表达式。
List(1,2,3).map { (x: Int) => x * 2 }
6、下划线_的用法
Scala是一门以java虚拟机(JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言。scala 单作为一门语言来看, 非常的简洁高效,在Scala中存在很多让代码更加简洁的语法,下划线_
便是其中一个。下划线的普遍用法总结如下:
(1)用于变量初始化:
在Java中,可以声明变量而不必给出初始值,在Scala中,变量在声明时必须显示指定,可以使用下划线对变量进行初始化。而且该语法只适用于成员变量,不适用于局部变量。例如:
class Student{
//String类型的默认值为null
var name : String = _
}
(2)用于导包引入:
导包引入时使用_
导入该包下所有内容,类比Java中的*
。例如:
//Java
import java.util.*;
//Scala
import java.util._
在导入某个包中的类时屏蔽某个类:
//例如在java.util包下将Date屏蔽,
import java.util.{Date =>_,_}
(3)用于将方法转变为函数:
在Scala中方法不是值,而函数是。所以一个方法不能赋值给一个val变量,而函数可以。方法可以转换为函数赋值给变量,例如:
//将println方法转换为函数,并赋值给p
var p = println _
(4)用于访问tuple元素:
val t = (1, 2, 3)
println(t._1, t._2, t._3)
(5)集合中的每一个元素:
val list=List(1,2,3,4)
val list1=list.map(_ * 10)
_
用来指代集合中的当前元素,用法有点像this。StringOps中的partition方法返回一对字符串,分别包含了满足某个条件和不满足某个条件的字符:在这个例子中,_就是依次指代构成字符串的每个字符。
scala> "New York".partition(_.isUpper)
res0: (String, String) = (NY,ew ork)
(6)用于简写函数:
如果函数的参数在函数体只出现一次,则可以用下划线代替。例如:
val nums = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
nums.map(_ + 2)
nums.sortWith(_>_)
nums.filter(_ % 2 == 0)
def valueAtOneQuarter(f: (Double) => Double) = f(0.25)
// 当我们将一个匿名函数传递给另一个函数或方法时,Scala会尽可能的帮助我们推断出类型信息。举例来说,我们不需要将代码写成
valueAtOneQuarter((x: Double) => 3 * x) //0.75
// 由于valueAtOneQuarter方法知道我们会传入一个类型为(Double)=>Double的函数,我们可以简单的写成:
valueAtOneQuarter((x) => 3 * x)
// 对于只有一个参数的函数,我们可以略去参数外围的():
valueAtOneQuarter(x => 3 * x)
// 如果参数咋=>右侧只出现了一次,我们可以使用_替换掉它:
valueAtOneQuarter(3 * _)
// 需要注意的是,以上的这些写法是在参数类型已知的情况下有效。
val fun = 3 * _ //错误,无法推断类型
val fun = 3 * (_: Double) //OK
val fun: (Double) => Double = 3 * _ //OK,因为已经给出了fun参数类型的定义
// 当然最后一个定义很造作。不过它展示了函数是如何被作为参数传入的。
(7)定义偏函数:
调用一个函数,实际上是在一些参数上应用这些函数。如果传递了所有期望的参数,就是对这个函数的完整应用,如果传递的参数比所要求的参数少,就会得到另外一个函数,就被成为偏应用函数。例如:
def showLog(d: Date, log: String): Unit = {
println("时间:" + d, ",日志信息:" + log)
}
val date = new Date()
//偏应用函数
showLog(date: Date, _: String)
(8)用于模式匹配:
在模式匹配中作为通配符,相当于Java中的case default
scala> val word="hadoop"
word: String = hadoop
scala> val result =word match{
| case "hadoop" => 1
| case "spark" => 2
| case _ => 0 //以上都没有匹配到才会被执行
| }
result: Int = 1
(9)下划线和其他符号组合的使用方式:
下划线与等号_=
,自定义setter方法,请参见《Overriding def with var in Scala》
下划线与星号_*
:
a.变长参数:
例如定义一个变长参数的方法sum,然后计算1-5的和,可以写为:
scala> def sum(args: Int*) = {
| var result = 0
| for (arg <- args) result += arg
| result
| }
sum: (args: Int*)Int
scala> val s = sum(1,2,3,4,5)
s: Int = 15
但是如果使用这种方式就会报错:
scala> val s = sum(1 to 5)
<console>:12: error: type mismatch;
found : scala.collection.immutable.Range.Inclusive
required: Int
val s = sum(1 to 5)
^
这种情况必须在后面写上: _*
将1 to 5
转化为参数序列:
scala> val s = sum(1 to 5: _*)
s: Int = 15
还有这种情况也是属于变长参数:将逗号前的_
转化为参数序列
scala> val list=List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)
scala> list match{
| case List(_,_*) =>5
| case _ =>2
| }
res1: Int = 5
b.变量声明中的模式:
例如,下面代码分别将arr中的第一个和第二个值赋给first和second:
scala> val arr = Array(1,2,3,4,5)
arr: Array[Int] = Array(1, 2, 3, 4, 5)
scala> val Array(1, 2, _*) = arr
scala> val Array(first, second, _*) = arr
first: Int = 1
second: Int = 2
参考:
【Scala】Scala中的_ 和 _*分别代表什么
Scala中下划线的7种用法
细数Scala下划线“_”的用法
Scala中的下划线使用总结
7、使用Option、Some、None,避免使用null
大多数语言都有一个特殊的关键字或者对象来表示一个对象引用的是“无”,在Java中它是null。在Java 里,null 是一个关键字,不是一个对象,所以对它调用任何方法都是非法的。但是这对语言设计者来说是一件令人疑惑的选择。为什么要在程序员希望返回一个对象的时候返回一个关键字呢?
为了让所有东西都是对象的目标更加一致,也为了遵循函数式编程的习惯,Scala鼓励你在变量和函数返回值可能不会引用任何值的时候使用Option类型。在没有值的时候,使用None,这是Option的一个子类。如果有值可以引用,就使用Some来包含这个值。Some也是Option的子类。
None被声明为一个对象,而不是一个类,因为我们只需要它的一个实例。这样,它多少有点像null关键字,但它却是一个实实在在的,有方法的对象。
Option类型的值通常作为Scala集合类型(List,Map等)操作的返回类型。比如Map的get方法:
scala> val capitals = Map("France"->"Paris", "Japan"->"Tokyo", "China"->"Beijing")
capitals: scala.collection.immutable.Map[String,String] = Map(France -> Paris, Japan -> Tokyo, China -> Beijing)
scala> capitals get "France"
res0: Option[String] = Some(Paris)
scala> capitals get "North Pole"
res1: Option[String] = None
Option有两个子类别,Some和None。当程序回传Some的时候,代表这个函式成功地给了你一个String,而你可以透过get()函数拿到那个String,如果程序返回的是None,则代表没有字符串可以给你。在返回None,也就是没有String给你的时候,如果你还硬要调用get()来取得 String 的话,Scala一样是会抛出一个NoSuchElementException异常给你的。
我们也可以选用另外一个方法,getOrElse。这个方法在这个Option是Some的实例时返回对应的值,而在是None的实例时返回传入的参数。换句话说,传入getOrElse的参数实际上是默认返回值。
scala> capitals get "North Pole" get
warning: there was one feature warning; re-run with -feature for details
java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:347)
at scala.None$.get(Option.scala:345)
... 33 elided
scala> capitals get "France" get
warning: there was one feature warning; re-run with -feature for details
res3: String = Paris
scala> (capitals get "North Pole") getOrElse "Oops"
res7: String = Oops
scala> capitals get "France" getOrElse "Oops"
res8: String = Paris
通过模式匹配分离可选值,如果匹配的值是Some的话,将Some里的值抽出赋给x变量:
def showCapital(x: Option[String]) = x match {
case Some(s) => s
case None => "?"
}
参考:【Scala】使用Option、Some、None,避免使用null
8、Scala 队列 toSeq() 方法及示例
在 Scala 编程语言中,队列是一种非常有用的数据结构。队列可以用于存储一些元素,并且可以按照指定的顺序进行访问。Scala 中提供了多种队列实现方式,其中包括可变队列和不可变队列。在本文中,我们将讨论 Scala 中队列的 toSeq() 方法及其用法示例。
(1)Scala可变队列toSeq()方法:
在 Scala 中,可变队列是一种允许进行插入和删除操作的队列。Scala 中的可变队列通过 mutable.Queue
类来实现。toSeq()
方法是 mutable.Queue
类的一个方法,用于将队列转换成一个序列 (Seq)
。下面的示例展示了 toSeq()
方法的用法:
import scala.collection.mutable.Queue
// 创建一个可变队列
val queue = Queue("a", "b", "c", "d")
// 调用toSeq方法将队列转换成序列
val seq = queue.toSeq
// 输出序列
seq.foreach(println)
执行结果如下:
a
b
c
d
在上面的示例中,我们创建了一个包含4个元素的可变队列,并且使用 toSeq()
方法将队列转换成序列。然后,我们使用 foreach()
方法遍历序列并输出其中的元素。
(2)Scala不可变队列toSeq()方法:
在 Scala 中,不可变队列是一种不允许进行插入和删除操作的队列。Scala 中的不可变队列通过 immutable.Queue
类来实现。toSeq()
方法同样也是 immutable.Queue
类的一个方法,用于将队列转换成一个序列 (Seq)
。下面的示例展示了 toSeq()
方法的用法:
import scala.collection.immutable.Queue
// 创建一个不可变队列
val queue = Queue("a", "b", "c", "d")
// 调用toSeq方法将队列转换成序列
val seq = queue.toSeq
// 输出序列
seq.foreach(println)
执行结果如下:
a
b
c
d
在上面的示例中,我们创建了一个包含4个元素的不可变队列,并且使用 toSeq()
方法将队列转换成序列。然后,我们使用 foreach()
方法遍历序列并输出其中的元素。
(3)Scala队列toSeq()方法的返回值
在 Scala 中,toSeq()
方法返回的是一个序列 (Seq)
。序列是一种线性的数据结构,其中的元素是有序的,并且每个元素都可以通过其在序列中的索引来访问。Scala 中可以使用多种序列类型,包括数组 (Array)
和列表 (List)
等。下面的示例展示了如何使用数组和列表来访问 toSeq()
方法返回的序列:
import scala.collection.mutable.Queue
// 创建一个可变队列
val queue = Queue("a", "b", "c", "d")
// 调用toSeq方法将队列转换成序列
val seq = queue.toSeq
// 使用数组访问序列
val array = seq.toArray
println(array(0))
println(array(1))
// 使用列表访问序列
val list = seq.toList
println(list(2))
println(list(3))
执行结果如下:
a
b
c
d
在上面的示例中,我们先创建了一个包含4个元素的可变队列,然后使用 toSeq()
方法将队列转换成序列。接着,我们分别使用数组和列表来访问序列中的元素。
9、Scala 中的 mkString
mkString(seq:String)
方法是将原字符串使用特定的字符串 seq 分割。
mkString(statrt:String,seq:String,end:String)
方法是将原字符串使用特定的字符串 seq 分割的同时,在原字符串之前添加字符串 start,在其后添加字符串 end。
object Test {
def main(args: Array[String]): Unit = {
var name : String = "Hello LittleLawson"
var age :Int = 2
println(name.mkString)
println(name.mkString(" "))//separate string with space
var str0 = "scala"
println(str0.mkString(","))//separate string with comma
println(str0.mkString("begin",",","end"))
/*
1.mkString is used in the inner List,That is say ,Elements in the list is applied.
*/
val a = List(1,2,3,4)
val b = new StringBuilder()
println(a.mkString("List(" , ", " , ")"))
}
}
执行结果如下:
Hello LittleLawson
H e l l o L i t t l e L a w s o n
s,c,a,l,a
begins,c,a,l,aend
List(1, 2, 3, 4)