大数据(十)--Scala编程语言-安装+基础

版权声明:本文版权归宋坤所有,转载请注明出处 https://blog.csdn.net/skisqibao/article/details/83414477

Scala介绍

  Scala是一门多范式的编程语言, 它与Java极其相似, 都是运行在JVM上的编程语言. Scala设计的初衷是将面向对象编程和函数式编程的各种特性集成起来。

  Scala是一门编译型语言, 同时也是一门弱类型语言, 它的语法简单, 但是灵活, 极大的灵活性使得初学者对其难以驾驭. 大数据中Spark底层是由Scala语言实现, 因此想要学习Spark, 要先入门Scala.

  Scala程序运行时, 会先编译成Java字节码文件, 然后该文件再在JVM上运行.

  Scala六个特征

  1. 可以和Java无缝混编
  2. 支持类型推测(自动推测类型)
  3. 并发和分布式(Actor, 类似Java中的Thread)
  4. 结合Java中interface实现和abstract继承的新特性trait
  5. 模式匹配match(类似Java中的switch, 但支持的类型更加丰富)
  6. 高阶函数(面向函数编程)
    - 函数参数是函数
    - 函数返回是函数

Scala安装

1. windows下安装, 环境配置

  官网下载scala2.10.4的压缩文件:https://www.scala-lang.org/download/2.10.4.html
  解压并配置环境变量(和配置jdk一样)
  新建SCALA_HOME
在这里插入图片描述
  上个步骤完成后,编辑Path变量,在后面追加如下:
  %SCALA_HOME%\bin;
在这里插入图片描述
  打开cmd,输入:scala - version 看是否显示版本号,确定是否安装成功
在这里插入图片描述
  可通过scala命令进入Scala Shell.

2. Scala-IDEA

  开发Scala程序, 可以去下载一个支持Scala的eclipse. 不推荐在现有的eclipse中安装Scala插件的形式.
  下载网址:http://scala-ide.org/download/sdk.html

3. IntelliJ IDEA中安装Scala插件

  打开idea,close项目后,点击Configure->Plugins
在这里插入图片描述
  搜索scala,点击Install安装
在这里插入图片描述

  或者直接在settings的plugins中搜索scala
在这里插入图片描述
  由于笔者之前以及安装过, 因此提示的是卸载(Uninstall), 如果之前未安装过会是一个绿色的√, 文字未Install.

4. IntelliJ IDEA-2017.3版本中创建Scala项目

  创建Scala项目
在这里插入图片描述
  设置项目名, 存放位置, JDK以及ScalaSDK
在这里插入图片描述
  在src下新建包, 右键新建Scala Class
在这里插入图片描述
  注意这里选择Object类型
在这里插入图片描述

Scala基础

1. 数据类型

Scala中与Java相似的数据类型

数据类型 描述 范围
Byte 8bit有符号数字 -128~127
Short 16bit有符号数字 -32768~32767
Int 32bit有符号数字 -2147483648~2147483647
Long 64bit有符号数字 -263~263-1
Float 32位IEEE754标准的单精度浮点数 1.4E-45~3.4028235E38
Double 64位IEEE754标准的双精度浮点数 4.9E-324~1.7976931348623157E308
Char 16位Unicode字符 U+0000~U+FFFF
String 字符串 -
Boolean 布尔类型 true/false
Null 空值或空引用, 是AnyRef的子类 -

Scala独有的数据类型

数据类型 描述
Unit 表示无值, 同void
Any 所有类型的超类, 任何实例都属于Any. Any的父类还是Object
AnyRef 所有引用类型的超类
AnyVal 所有值类型的超类
Nothing 表示没有值, 所有其他类型的子类型, 该类型的对象不会报空指针异常
None Option的子类,用于安全的函数返回值
some Option的子类, 用于存储元组数据(“hello”,“scala”), 类似于map,由一对小括号包括

数据类型类图
在这里插入图片描述

2. 常量&变量

  定义变量: var a: Int = 1
  定义常量: val b: Int = 1
  由于Scala支持类型推测, 因此常量或变量的类型一般省略, 上述定义可简写为:
    var a = 1
    val b = 1
  注意:
    val修饰的常量一旦定义, 则其值不可修改. 如果修改, 会报error: reassignment to val.
    Scala中语句之后可以不加分号’;’ , 自动根据换行符区分. 但是, 如果多条语句在一行, 必须加分号隔开.

3. 类&对象

  创建类

class Person {
	val name = "qibao"
	val age = 22
	def tell() = {
		"My name is " + name + ". My age is " + age
	}
}

   val 声明两个常量, 不可修改.
   def 声明函数, 具体格式之后再说.

  创建对象

object OnePerson {
	def main(args: Array[String]): Unit = {
		val person = new Person()
		println(person.name + ":" + person.tell())
	}
} 

注意:

  1. 命名规则与Java保持一致即可, 包括类首字母都是大写, 方法首字母小写, 符合驼峰式命名法.
  2. Scala中class默认可传参, 默认的传参数就是默认的构造函数. 重载构造函数时, 必须调用默认构造函数.
//定义类时传参
class Person(var name: String, var age: Int) {
  var fcp = 0.0
  //重载构造器, 参数不能有修饰符
  def this(name: String, age:Int, facePower:Double) = {
  	//调用默认构造器
    this(name,age)
    fcp = facePower
  }
  def tell() = println(name + ":" + age + ":" + fcp)
}
  1. 在class修饰的类中, 如果参数用var来修饰, 那么可以之间通过对象名.属性来调用; 如果参数用val修饰, 则不能通过对象名.属性来修改; 如果参数没有修饰符, 则该属性为私有, 无法通过对象来调用.
object ScalaBase {
  def main(args: Array[String]): Unit = {
    var p1 = new Person("qb", 22, '男')
    p1.name = "qibao"
//    p1.age = 23   val定义无法修改
    println(p1.age)
//    println(p1.sex) 没有修饰符,无法访问
  }
}

class Person(var name: String, val age: Int, sex: Char) {
  def tell() = println(name + ":" + age)
}

  1. Scala中object修饰的单例对象, 不可传递参数. 在Scala中没有static这一关键字, 所有静态属性和方法都需要放在object修饰的对象中.
  2. 创建object修饰的对象时, 不用new这一关键字; 创建class修饰的对象时, 使用new, 且在new的时候, 除了class中的方法不执行外, 其余的都执行.
  3. 如果在同一个文件中, object对象和class类的名称相同, 则这个对象就是这个类的伴生对象, 这个类就是这个对象的伴生类. 伴生对象和伴生类之间可以互相访问私有变量.
  4. Scala中伴生对象+伴生类 = Java中某个类
  5. apply方法的使用: apply相当于Java中的静态代码块, 创建对象时自动调用. 因此它要在object修饰的伴生对象中定义.
class Person(var name: String, var age: Int) {
  def tell() = println(name + ":" + age)
}
object Person {
  def apply(name: String, age: Int) = new Person(name, age)
}

定义之后再创建Person对象时, 可以省略new关键字
object ScalaBase {
	def main(args: Array[String]): Unit = {
	   var p1 = new Person("qb", 22)
	   var p2 = Person("qb", 23)
	  }
}

4. 判断语句

  判断语句与Java中的用法完全一致, 两种形式 if-else和if-else if -else.

 val age =18 
 if (age < 18 ){
  	println("no allow")
  }else if (18 <= age && age <= 20){
  	println("allow with other")
  }else{
  	println("allow self")
  }

5.循环语句

5.1 to和until的使用

  这俩个方法都会产生一个集合, 其中until前闭后开, to前后均闭.
  比如说,1 until 10 返回1到9的数组, 1 to 10 返回1到10的数组.

println(1 to 5 )//打印 1, 2, 3, 4, 5
println(1.to(5))//与上面等价,打印 1, 2, 3, 4, 5
   
println(1 until 5 ) //不包含最后一个数,打印 1,2,3,4
println(1.until(5))//与上面等价
    

  除按顺序打印外, 还可设置步长.

println(1 to (10 ,2))//步长为2,从1开始打印 ,1,3,5,7,9
println(1.to(10, 2)) 

println(1 until (10 ,3 ))//步长为3,从1开始打印,打印1,4,7

5.2 for循环

  1. 通过索引
 var list = List("hello", "scala", "spark")
 for (index <- 0 until list.length) println(list(index))
  1. 增强for循环
for (elem <- list) println(elem)

  在用Scala写for循环时, 不需要指定循环变量的类型, Scala会自动推测出其类型, 同时给循环变量赋值使用"<-". Scala习惯来讲, 如果判断或循环语句只有一行, 一般把它们放在一行.

  1. 多层for循环
      当有多层循环时, 可用分号’;'将循环变量隔开.
//打印九九乘法表
for (i <- 1 to 9; j <- 1 to 9) {
      if (j <= i) print(i + "*" + j + "=" + i * j + "\t")
      if (j == i) println()
    }
  1. for循环中添加条件判断
      Scala的for循环, 还可以在循环变量设置的( ) 中添加条件判断, 只需用分号’;'将它们隔开即可.
for (i <- 1 to 100; if i % 2 == 0) println(i)

for (i <- 1 to 10 ; if i % 2 == 0 ;if i == 4 ) println(i)
  1. yield关键词自动封装集合
      在for循环中使用yield关键词后, 能够将符合要求的元素自动封装到一个集合中.
//将1-20之间的偶数封装到rest集合中
val rest = for (i <- 1 to 20; if i % 2 == 0) yield i
for (elem <- rest) println(elem)

5.3 while和do…while循环

  Scala中while和do-while循环的用法与Java中也是完全一致的, 唯一一点需要注意的是Scala语法中没有break语句, 因此想要跳出循环可以自定义一个布尔类型的标识符.

var flag = true
var index = 0
while (flag && index < 50){
   index += 1
   if (index == 20) flag = false
   println(index)
}

  通过上述几个实例, 可以看出Scala语法的灵活性以及在写Scala代码时遵循的一些简写习惯, 之后在函数板块会有更多简写规则, 更能体现Scala的灵活.

Scala函数

1. 函数定义

在这里插入图片描述

  1. 函数定义用def关键词.
  2. 可有参也可无参,传参时需指定参数类型.
  3. 函数可写返回值类型也可不写,Scala会自动推断. 但有时候不能省必须写,比如在递归函数或函数的返回值是函数类型的时候.
  4. Scala中函数有返回值时,return可写可不写. 如果不写, 函数会把最后一行当做结果返回。当写return时,必须要写函数的返回值.
  5. 如果函数体只有一行,可以将方法名和函数体写在一行.
  6. 传递给方法的参数可以在方法中使用,并且scala规定方法传过来的参数为val的,不是var的。
  7. 如果去掉方法体前面的等号,那么这个方法返回类型必定是Unit的。这种说法无论方法体里面什么逻辑都成立,scala可以把任意类型转换为Unit.假设,里面的逻辑最后返回了一个string,那么这个返回值会被转换成Unit,并且值会被丢弃。
 def fun1(a: Int, b: Int): Int = {
   return a + b;
 }

 def fun2(a: Int, b: Int) = {
   a + b
 }

 def fun3(a: Int, b: Int) = a + b

  从f1–>f3, 由Java编码思想向Scala编码思想的转变. Scala追求的是一种高效, 快速的编码习惯.

2. 递归函数

  递归函数: 函数自身调用自身

//求一个数的阶乘
def fun4(n: Int): Int = {
    if (n == 1 || n == 0) 1
    else n * fun4(n - 1)
  }

  需要注意的是, 递归函数在最后一次返回之前返回的是一个函数, 此时Scala无法推测其类型. 因此, 在Scala中写递归函数时, 必须明确返回值类型, 否则会报Type mismatch的错.

3. 带默认值的函数

  默认值函数: 函数在定义时, 指定某个或多个参数的值.

object ScalaFun1 {
  def main(args: Array[String]): Unit = {
    println(fun5());
    println(fun5(1));
    println(fun5(b=2));
    println(fun5(1,2));
    
    println(fun5_1(1));
    println(fun5_2(b = 2));
  }
  //默认值函数
  def fun5(a: Int = 10, b: Int = 20) = a + b

  def fun5_1(a: Int, b: Int = 20) = a + b

  def fun5_2(a: Int = 10, b: Int) = a + b
}

  注意: 当参数有默认值时, 可以不指定, 也可指定. 但如果想要给不是第一个位置的参数指定值时, 需要明确参数的名称.

4. 可变参数列表的函数

  可变参数列表函数: 函数参数可以是一个也可以是多个. Java中使用…来指定可变参数列表方法, Scala中使用*.

def fun6(args: Double*) = {
    var sum = 0.0
    for (arg <- args) sum += arg
    sum
  }

  注意: Scala中+=, -=前后的数值类型必须相同, Scala不会隐式类型转换; +,- 可以前后类型不相同. 此外, 函数最后一行作为返回值, for的返回是Unit(空), 因此需要另起一行返回sum.
  补充一点: Scala中没有++, --运算符.

5. 匿名函数

  匿名函数: 没有名字的函数. 既然没有名字就无法调用, 因此可以将匿名函数赋给一个变量.

def main(args: Array[String]): Unit = {
    println(fun7(1, 2));
  }
val fun7 = (a: Int, b: Int) => a + b

  注意:

  1. 匿名函数可以有参, 也可无参
  2. 匿名函数的参数和方法体要用 => 来连接
  3. 匿名函数不能显示的声明返回值类型

6. 嵌套函数

  匿名函数: 函数体内又定义了一个函数.

def fun8 (n:Int) = {
    def fun9(a:Int,b:Int):Int ={
      if(a==1) b
      else fun9(a-1,a*b)
    }

    fun9(n,1)
  }

7. 偏应用函数

  偏应用函数: 偏应用函数是一种表达式, 它实际是调用了其他函数. 只不过不需要提供函数所需的全部参数, 只需提供部分或不提供参数, 不提供的参数单独指定.

//普通函数
  def log(date: Date, content: String) = {
    println("date" + date + "\tcontent" + content)
  }

  val date = new Date()
  log(date,"log1")
  log(date,"log2")
  log(date,"log3")
  
  //偏应用函数
  val logBoundDate = log(date,_:String)
  logBoundDate("log1_1")
  logBoundDate("log2_1")
  logBoundDate("log3_1")

  分析代码可知, 偏应用并不是真实的函数, 而是将某个函数的一个或多个参数指定后的表达式. 上述需求中, 需要传入时间和内容作为日志, 但时间往往是不变的, 变化的只是内容, 因此可以使用偏应用函数来处理. 其中’_’ 代表占位符, 指代第二个参数.

8. 高阶函数

  高阶函数: 高阶函数是指(1)函数的参数是函数; (2)函数的返回类型是函数; (3)函数的参数和返回类型是函数 的函数.

  函数作为参数或返回值进行传递时, 也是没有名字的, 因此也相当于是匿名函数, 上文也说过, 匿名函数的参数和方法体之间用 => 进行连接.

  • 函数的参数是函数
def highFun1(f: (Int) => Int, num: Int) = f(num)
def temF1(num: Int) = num + 1

//调用函数
println(highFun1(temF1, 1))

  代码分析: highFun1有两个参数, 其中一个参数是函数, 函数的类型匹配参数为Int, 返回为Int的函数. 由此可以看出, Scala把函数当成对象在函数之间传递, 这也是Scala函数式编程的体现.
  同时在使用高阶函数时, 函数参数直接写函数名, 不需要加括号(), 加括号说明是调用函数, 使用的是函数返回的结果, 直接写函数名才是将函数切切实实地作为参数进行传递.

  • 函数的返回类型是函数
def highFun2(num: Int): (Int, Int) => Double = {
    def temF2(num1: Int, num2: Int): Double = {
      num + num1 + num2
    }

    temF2
  }

//调用函数
var fun = highFun2(1)
println(fun(1,1))
//上述两行代码也可简写为
println(highFun2(1)(1,1))

  代码分析: highFun2有一个Int类型的参数, 返回一个(Int, Int) => Double格式的函数, 返回时可以像上述代码中写的一样, 主动定义一个函数, 然后返回, 也可以直接在最后一行写一个匿名函数返回. 如下:

def highFun3(num: Int): (Int, Int) => Double = {
	(num1: Int, num2: Int) => num1 + num2 + num
}

  调用highFun2时, 它返回一个函数, 这就相当于匿名函数, 可以通过一个变量来接收这个函数, 然后再通过给这个变量设置参数的形式得到返回函数的值.

  • 函数的参数和返回类型都是函数
def highFun4(f: (Int,Int) => Int, num1: Int): (Int) => Double = {
    (num: Int) => num + f(num1,1)
 }

//调用函数
var fun = highFun4((a:Int,b:Int)=>a+b,1)
var res0 = fun(1)
println(res0)
//上述可简化
var res1 = highFun4((a:Int,b:Int)=>a+b,1)(1)
println(res1)
//如果匿名函数的参数在方法体中只使用了一次 那么可以写成_表示    
var res2 = highFun4(_+_,2)(2)
println(res2)

  highFun4的参数类型和返回类型不用再说了, 主要是函数调用时代码简化的过程, highFun4的匿名函数参数已经指定该函数有两个参数(这句话比较拗口, 建议读者慢慢领会), 在调用highFun4时, 如果这个函数参数的两个参数只使用一次, 那么可以用 _ 来表示这些参数.
  Scala学习地越深入, 越容易发现Scala许多操作类似jQuery中的链式操作, 而且有些内容能省则省, 这就于人类日常交流类似, 双方都知道的事情, 就没有必要每次说话时再点出来.

9. 柯里化函数

  柯里化函数: 柯里化函数实质上是一类高阶函数的简化.
  先来看这一类高阶函数:

def highFun5(num: Int): (Int) => Int = {
    def fun(a: Int) = num + a
    fun
}

//调用时,可以将两个参数写一行
println(highFun5(1)(2))

  再来看柯里化函数:

def klhFun(a: Int)(b: Int) = a + b

//调用方式跟上述代码一致
println(klhFun(1)(2))

  满足上述样式的函数就是柯里化函数, 再比如:

 def fun7(a :Int,b:Int)(c:Int,d:Int) = {
      a+b+c+d
    }
println(fun7(1,2)(3,4))

猜你喜欢

转载自blog.csdn.net/skisqibao/article/details/83414477