zoukankan      html  css  js  c++  java
  • 大数据-02-Scala入门

    Scala 简介

    它是一门基于JVM的面向函数和面向对象的编程语言, 它包含了求值表达式,闭包,切片操作,模式匹配,隐式转换等特性。

    • 可变量/不可变量
    • 可变集合/不可变集合、集合操作
    • 函数
    • 值函数
    • 求值表达式
    • 函数柯里化
    • 偏部分应用函数
    • 偏函数
    • 闭包
    • 类(封装、继承、多态)
    • 特质
    • 单例类
    • 伴生单例
    • 模式匹配(_, * , reg)
    • 隐匿转换

    Scala 安装

    环境
    服务器:ubuntu-16.04.3-desktop-amd64.iso
    我们将要安装的Scala版本是2.10.7, 确保你本地以及安装了Java 8 JDK 版本,并且设置了 JAVA_HOME 环境变量及 JDK 的bin目录。

    安装JDK

    Ubuntu 16.04安装Java JDK
    Java JDK有两个版本,一个开源版本Openjdk,还有一个oracle官方版本jdk。下面记录在Ubuntu 16.04上安装Java JDK的步骤。
    安装openjdk
    更新软件包列表:

    sudo apt-get update
    

    安装openjdk-8-jdk:

    sudo apt-get install openjdk-8-jdk
    

    查看java版本:

    java -version
    

    直接安装Scala

    下载地址https://downloads.lightbend.com/scala/2.10.7/scala-2.10.7.tgz, 并将其解压缩到指定位置:

    sudo mkdir /usr/local/scala    # 创建一个根目录
    sudo tar -zxf scala-2.10.7.tgz -C /usr/local/scala  # 解压缩到指定位置
    

    (可选)下载scala集成开发环境
    我这里是下载IntelliJ IDEA >社区版本,https://www.jetbrains.com/idea/download 下载后解压缩包,在bin文件夹下运行./idea.sh启动,然后在引导页面选择添加Scala支持

    添加scala到环境变量

    打~/.bashrc文件,在尾部添加如下内容

    export SCALA_HOME=/usr/local/scala/scala-2.10.7
    export PATH=${SCALA_HOME}/bin:$PATH
    

    然后使环境变量生效

    source ~/.bashrc
    

    检查scala是否安装成功

    scala -version
    

    结果如图所示:

    简单使用

    进入命令解释窗口:

    scala 变量

    不可变变量 val

    val pi = 3.14
    

    可变变量 var

    在scala中一切皆对象,即使数字都有方法。

    var name = "John"
    name = "rose"
    var age = 30  # 但是没有age++这种写法
    
    2.to(5)    # res3: ...Inclusive = Range(2, 3, 4, 5)
    1.+(1)     # 1+1 = 2, 
    

    可变与不可变集合的对应关系

    不可变集合

    不可变集合一旦被创建,便不能改变;添加,删除,修改元素操作返回的是新集合。
    默认就是不可变集合scala.collection.immutable包。

    val arr = Array[Int](10,20)
    val arr1 = arr :+ 1  #  返回一个追加了元素的新集合, 这里:表示原集合,+表示附加操作
    val arr1 = 1 :+ arr  #  返回一个追加了元素的新集合, 这里:表示原集合,+表示附加操作
    val arr1 = arr ++ arr  #  ++ 表示连接
    

    可变集合

    可以添加,删除,修改元素作用于原集合
    如果要使用可变集合import scala.collection.mutable

    val arr = mutable.ArrayBuffer[Int](10,20)
    arr += 10   # 添加元素
    val mutableSet = mutable.Set(1,2,3)
    

    定长数组

    数组的长度在定义时已经确定, 在运行时长度不能被修改。能过new来进行默认初始化

    val numArr = new Array[Int](10)
    val strArr = new Array[String](10)
    

    变长数组

    变长数组在程序运行过程中, 数组的长度是可以进行调整的,最常用的变长数组是ArrayBuffer, 使用它前必需显示的引用包 import scala.collection.mutable._ 如下所示:

    import scala.collection.mutable._
    val strArr = ArrayBuffer[String]()
    strArr += "hello"  # 追加一个对象
    # 用++= 来追加一个集合
    strArr ++= Array("Welcome", "to", "china")
    strArr.trimEnd(2)  # 删除末尾2个对象
    # 遍历数组
    for (i <- 0 until strArr.length) println(i)
    # 常用函数
    strArr.min
    strArr.max
    strArr.toString
    strArr.mkString
    

    列表List

    val liststr = List("Spark", "Hive", "Flink")
    val liststr = "Spark" ::( "Hive" :: Nil)  # 也可以用如下方式进行创建
    # 常用函数有:
    liststr.isEmpty
    liststr.head
    liststr.tail
    liststr.reverse
    # 连接两个List
     List("hello") ::: List("Boys" , "and", "Girls")
    # 丢弃前n个元素
    liststr.drop(1)
    # zip操作
    val nums = List(1,2,3)
    val chars = List('1','2','3')
    val z = nums zip chars #结果为z: List[(Int, Char)] = List((1,1), (2,2), (3,3))
    # 伴生对像方法 
    List.range(2,6)  # 构建某一值范围内的List
    List.range(2,6,2) # 加上步长参数
    List.make(5, "boy") # 构建多个相同元素的List
    List.unzip(z)    # unzip 前面的z不变量
    

    函数

    scala对象的构造可以使用apply函数, 而不需要new操作, 以下两行是等价的:

    val arr = Array(1,2,3,4)  
    val arr = Array.apply(1,2,3,4)  
    

    表达式

    在scala表达式的是有结果的

    var age = 30
    val result = if (age >= 18) "adult" else "child"
    val result = if (age >= 18) {1|2|3}  # 最后一语句的值就是整个表达式的值
    for (i <- 0 to 10 if i%2 == 0) {print(i)} # 产生集合 <- 是生成器generator 
    for (i <- 0 until (10,2)) {print(i)} # until 不右包含, 步长为2
    var x = for (i <- 0 until (10,2)) yeild i # 缓存结果i成一个vector集合
    import scala.util.control.Breaks._  #SCALA 没有break, 要引入Break对像
    for (i <- 0 to 10) {if (i == 4) break ; print(i)} # 产生集合,输出,
    
    

    Scala 函数

    Scala 函数声明格式如下, 方括号为可选

    def functionName ([参数列表]) : [return type]

    Scala 函数定义格式如下:

    def functionName ([参数列表]) : [return type] = {
    function body
    return [expr] # 最后一句可以省略return 作为返回值
    }

    例子如下:

    def printMe( ) : Unit = {
    println("Hello, Scala!")
    }

    def sum(numbers : Int*) = {
    var result = 0;
    for (elem <- numbers) result += elem;
    result
    }

    Scala 值函数 (函数字面量 or 函数变量 or 函数指针)

    Scala 值函数, 本质上它是一个量指向一段计算代码,定义格式如下:

    val literalName = ([参数列表]) > {
    body
    expr # 作为结果值
    }

    它没有返回类, 而是一个映射, 通过=> 符号将左边类型转到右边类型。如果只有一行是{}可以省略; 形如(x:Int,y:Int)=>{x+y}的表达式称为Lambda表达式。
    将值函数(函数变量)作为另一个函数的输入参数:

    val increment=(x:Int)=>x+1
    val arrInt = Array(1,2,3,4)
    arrInt.map(increment) # 这是map就是高阶函数,即参数或者返回值含有值函数
    # 其它高阶函数
    arrInt.flatMap # 将集合中各元素得到一个集合,并扁平合并成一个集合
    arrInt.reduce  # 使用2元操作在一个集合上,返回一个值
    arrInt.filter  # 返回满足条件的集合值
    arrInt.fold    # 带有初始值的reduce
    
    

    闭包 (Closure)

    John D.Cook 给对象和闭包下过一个经典的定义:"An object is data with function. A closure is a function with data", 由此闭包是由函数和运行时数据决定的。 事实上, 闭包可以理解为函数和它的上下文, 例如:

    var i = 15
    val f=(x:Int)=>x+i
    f(10)    # 结果是25
    

    函数柯里化(currying)(就是括号化, 很多多括号)

    在前面所述, 当高价函数返回的是一个函数对象是, 就可能会存在fun(1)(2)这种调用方式, 那么如果显示声明这样一个形式的函数,也就是函数柯里化, 它的定义形式如下:

    def curryFunctionName (param_x:Int)(param_y:Int)(param_z:Int):Int = {
    }
    

    实例如下:

    def mul(x:Int)(y:Int):Int={x * y}
    

    由此可见, 调用柯里化函数时, 参数必须要示完整。所以它不具备传部分参数,然后返回某个对象的能力。

    部分应用函数

    为了应对柯里化函数对完整参数的限制, 提出部分应用函数, 即先输入部分参数, 调用返回一个中间对象, 然后通过中间对象进行后续调用, 使得柯里化+部分应用函数类似高阶函数的表现形式:

    # 生成一个部分应用函数
    val paf = mul(10) _
    # 调用部分应用函数
    paf(5)    # 结果为50
    

    不只柯里化函数有部分应用函数, 普通函数也有部分函数, 例如:

    def product (x1:Int, x2:Int, x3:Int) = x1 * x2 * x3
    val paf = product(_:Int, 2, 3)
    paf(2)   # 结果12
    

    偏函数

    它是数学函数上的,自变量与因变量, 当在值域找不到对应目标时,就报错。

    trait PartialFunction[-A, +B] extends (A) ⇒ B
    

    示例如下:

    val isEven : PartialFunction[Int, String] = {
        case x if x % 2 == 0 => x + "is even"
    }
    isEven(10)  # 这个OK
    isEven(11)  # 这里报错
    

    Scala 下划线

    1、存在性类型:Existential types
    def foo(l: List[Option[_]]) = ...

    2、高阶类型参数:Higher kinded type parameters
    case class A[K[_],T](a: K[T])

    3、临时变量:Ignored variables
    val _ = 5

    4、临时参数:Ignored parameters
    List(1, 2, 3) foreach { _ => println("Hi") }

    5、通配模式:Wildcard patterns
    Some(5) match { case Some() => println("Yes") }
    match {
    case List(1,
    ,) => " a list with three element and the first >element is 1"
    case List(
    *) => " a list with zero or more elements "
    case Map[,] => " matches a map with any key type and any value >type "
    case _ =>
    }
    val (a, ) = (1, 2)
    for (
    <- 1 to 10)

    6、通配导入:Wildcard imports
    import java.util._

    7、隐藏导入:Hiding imports
    // Imports all the members of the object Fun but renames Foo to Bar
    import com.test.Fun.{ Foo => Bar , _ }

    // Imports all the members except Foo. To exclude a member rename it to >_
    import com.test.Fun.{ Foo => _ , _ }

    8、连接字母和标点符号:Joining letters to punctuation
    def bang_!(x: Int) = 5

    9、占位符语法:Placeholder syntax
    List(1, 2, 3) map (_ + 2)
    _ + _
    ( (: Int) + (: Int) )(2,3)

    val nums = List(1,2,3,4,5,6,7,8,9,10)

    nums map (_ + 2)
    nums sortWith(>)
    nums filter (_ % 2 == 0)
    nums reduceLeft(+)
    nums reduce (_ + )
    nums reduceLeft(
    max )
    nums.exists(
    > 5)
    nums.takeWhile(_ < 8)

    10、偏应用函数:Partially applied functions
    def fun = {
    // Some code
    }
    val funLike = fun _

    List(1, 2, 3) foreach println _

    1 to 5 map (10 * _)

    //List("foo", "bar", "baz").map(_.toUpperCase())
    List("foo", "bar", "baz").map(n => n.toUpperCase())

    11、初始化默认值:default value
    var i: Int = _

    12、作为参数名:
    //访问map
    var m3 = Map((1,100), (2,200))
    for(e<-m3) println(e._1 + ": " + e._2)
    m3 filter (e=>e.1>1)
    m3 filterKeys (
    >1)
    m3.map(e=>(e._1*10, e._2))
    m3 map (e=>e._2)

    //访问元组:tuple getters
    (1,2)._2

    13、参数序列:parameters Sequence
    *作为一个整体,告诉编译器你希望将某个参数当作参数序列处理。例如val s = >sum(1 to 5:)就是将1 to 5当作参数序列处理。
    //Range转换为List
    List(1 to 5:_
    )

    //Range转换为Vector
    Vector(1 to 5: _*)

    //可变参数中
    def capitalizeAll(args: String*) = {
    args.map { arg =>
    arg.capitalize
    }
    }

    val arr = Array("what's", "up", "doc?")
    capitalizeAll(arr: _*)

    Scala 面向对象编程

    类的定义

    Scalal中的类与Java语言一样通过class来定义:

    import scala.beans.BeanProperty   // 如果使用BeanProperty需要引入
    
    class Person{
        // 成员变量必须初始化, 这里会生成scala的getter与setter
        var name:String = null  // getter就是name, setter就是name_
        @BeanProperty var age:Int = 0 //这里会生成Java风格的setAge(),getAge()
        
    }
    

    类成员访问

    val p = new Person
    p.name_= ("John")  // 显示调用setter方法修改成员变量, 这里是_= () 方法
    p.name = "John"    // 直接修改成员变量name, 实际也是调用p.name_= 方法
    p.setAge(1)
    p.getAge()
    

    主构造函数

    为了简化构造函数与类定义, scala 提供了主构造函数,集定义与构造于一身:

    // 注意有个var关键字
    class Person(var name:String, var age:Int) //这就完成了上述声明与构造。
    class Person(var name:String, var age:Int=18)//这样就带默认参数了
    

    辅助构造函数

    为了实现多个构造函数,且不带默认参数的, 提出辅助构造函数。连带好处还避免多处使用类名字的问题, 就是使用this关键字:

    class Person{
        private age:Int = 18
        private name:String = null
        def this(name:String){  # 这里就是辅助构造函数
            this()              # 调用无参的默认主构造函数
            this.name = name
        }
        def this(name:String,age:Int){
            this(name)
            this.age = age
        }
    }
    

    继承 和 多态

    # 注意新添加字段有var关键, 继承的没有,构造时为传递参数值
    class Student(name:String, age:Int, var studentNo:String) extends Person(name,age){
     override def toString:String = super.toString + ", studentNo=" + studentNo
    }
    var p = new Person("bill")
    p = new Student("John", 33, "12345")
    p.toString
    

    注: 如果类或者trait声明前加sealed 关键字,则其所有派生都必须放在同一个文件中

    默认访问控制(隐藏)

    public : 是默认选项, 类,伴生单例,子类, 外部都可以访问
    protected : 类,伴生单例,子类可以访问,外部不可以访问
    private : 类,伴生单例可以访问,子类,外部不可以访问
    private[this]: 类可以访问,伴生单例,子类,外部不可以访问

    访问控制符,还可以放在主构造函数中,起到相信的效果:

    // 这里name的控制符是protected,而age就是默认的控制符 public
    class Person(protected var name:String, var age:Int) 
    // 如果在主构造函数中, 未使用val ,var关键字来定义字段,就是默认为private[this]
    class Person(name:String, age:Int)
    

    抽象类

    就是用abstract来定义类, 抽象类可以拥有抽象成员,即不初始化成员

    abstract class Person{
      var age:Int = 0   // 具体成员
      var name:String   // 抽象成员
      def walk()        // 抽象成员
      override def toString = name // 具体成员
    }
    

    单例对象(类)

    在java中经常会用到静态成员变量, 但是在Scala没有并不支持这项特性, 取而代之的是单例对象, 它是通过object 来声明的, 示例如下:

    object GlobalID { var gid:Int = 0 }
    GlobalID.gid
    

    伴生对象与伴生类

    就是名字相同的类与单例对象,可以相互访问私有成员。它们分别叫做伴生类与伴生对象(单例)。 类似C++的friend关系

    class Dog {
      private var name = "张"
    
      def printName(): Unit = { //在Dog类中访问其伴生对象的私有属性
        (Dog.CONSTART + name)
      }
    }
    
    object Dog {
      private val CONSTART = "汪"
      def main(args: Array[String]): Unit = {
        val p = new Dog
        println(p.name)
        p.name = "大黄"
        p.printName()
      }
    }
    

    trait 特质

    Scala用trait关键字封装了成员方法和成员变量, 在类通过extends或with关键字来混入trait。一个类可以混入多个trait。
    它起到了interface接口的作用,以及struct结构嵌入的作用等。
    它的定义示例如下:

    // 可克隆特质
    trait Closable{
      def close():Unit
    }
    
    // 通过extends 来实现特质
    class File(var name:String) extends Closable{
      def close():Unit = println(s"File $name has been closed")
    }
    // 也可以如下
    class File(var name:String) extends java.io.File(name) with Closable{
      def close():Unit = println(s"File $name has been closed")
    }
    // 在trait类似抽象类, 在trait中可以有抽象成员, 成员方法, 具体成员和具体方法。
    trait PersonDAO{
    // 具体成员变量
    var recordNum:Long = _
    // 具体成员方法
    def add(p:Person):Unit={
      println("Invoking add Method..")
    }
    // 抽象方法 更新方法
    def update(p:Person)
    // 抽象方法 删除方法
    def delete (id:Int)
    }
    

    注意, 使用trait出现菱形继承问题时, 使用的是最右深度优先遍历算法查找调用的方法。

    提前定义与懒加载

    提前定义是指在常规构造之前将变量初始化, 下述的构造会出现空指针。

    class FileLogger{
    var fileName : String
    val fileOutput = new PrintWriter(fileName:String)
    def log(msg:String) : Unit = {
      fileOutput.print(msg)
      fileOutput.flush()
    }
    }
    
    // 正常构造使用,就会报错
    val s = new {
      // 提前定义
      override val fileName = "file.log"
    } with FileLogger
    s.log("predefined variable")
    

    懒加载, 上述提前定义的方式不够优雅, 推荐使用下面的懒加载方式

    class FileLogger{
    var fileName : String
    // 这里的语句的不会执行,只等真正使用该变量时才会执行
    lazy val fileOutput = new PrintWriter(fileName:String)
    def log(msg:String) : Unit = {
      fileOutput.print(msg)
      fileOutput.flush()
    }
    }
    
    val s = new FileLogger
    s.fileName = "file.log"
    s.log("predefined variable")
    
    

    模式匹配

    匹配是一个以match关键连接的简单表达式, 结果为match匹配的执行结果

    // 1.简单匹配
    val (first, second) = (1,2)
    // 2.常量匹配
    for (i <- 1 to 5){
     i match{
      // 常量匹配
      case 1 => println(1)
      // 3.变量匹配
      case x if (x%2 == 0) => println(s"Mode 2 equal zero")
      case _ => printlen("other")
     }
    }
    // 4.case类匹配
    case class Dog(val name:String, val age:Int)   // 以case关键字定义开始
    case class Person(var name:String, var age:Int) 
    case class Student(var name:String, var age:Int) 
    def myMatch(x:AnyRef) = x match{
      // 4.1case类构造匹配
      case Dog(name, age) => println(s"Dog name=$name, age=$age")
      // 4.2case类类型匹配
      case x:Person => pringln(s"this is a person")
      // 4.3case类变量匹配
      case s@Student(_,_) => println(s"Student:" + s)
      case _ => println("other")
    }
    val dog = new Dog("jacky", 11)
    val p = new Person("Peter", 22)
    val std = new Student("Jim", 33)
    myMatch(dog)
    myMatch(p)
    myMatch(std)
    // 5.序列模式
    
    def myMatch(x:AnyRef) = x match{
      // 序列模式
      case Array(first,second) => println(s"first:$first, second:$second")
      case Array(first,_,third,_*) => println(s"first:$first, third:$third")
      // 6.元组模式
      case (first,second) => println(s"tuple first:$first, second:$second")
      case (first,_, third) => println(s"tuple first:$first, third:$third")
      case _ =>
    }
    val arr = Array(1,2,3,4)
    myMatch(arr)
    val arr = Array(1,2)
    myMatch(arr)
    val tuple = ("nest",1)
    myMatch(tuple)
    

    正则表达式

    val rgex = """(dddd)-(dd)-(dd)""".r  // .r 将字符串转化正则表达式
    // 以下是几个常用方法
    for (date <- rgex.findAllIn("2015-12-31 2016-02-30")) println(date)
    for (date <- rgex findAllMatchIn "2015-12-31 2016-02-30") println(date.groupCount)
    

    for 中的模式匹配

    // 常量匹配
    for ((name, 18)<-Map("jack"->22, "tom"->18, "ben"->7)) println(name)
    // 变量匹配
    for ((name,age)<-Map("jack"->22, "tom"->18, "ben"->7)) println(name,age)
    // 变量绑定
    for ((name, e@7)<-Map("jack"->22, "tom"->18, "ben"->7)) println(name,e)
    // 类型匹配
    for ((name, age:Int)<-Map("jack"->22, "tom"->18, "ben"->7)) println(name,age)
    // 构造匹配
    // 序列匹配
    

    隐式转换

    隐式转换函数 - 将参数类型转换为返回类型

    // 隐式转换函数的定义
    // 它是通过参数类型与返回类型确定的
    // 因而不能有这样的两个函数,它们参数与返回值类型都相同
    implicit def int2float(x:Int):Float = x.toFloat 
    

    隐式转换类 - 将参数类型转换为隐式类类名类型

    // 这就是把字符串类型转换为Dog类
    implicit class Dog(val name:String)
    

    应用程序对象

    object MyApp {
     def main(args:Array[String]){
        println("App v1")
     }
    }
    
  • 相关阅读:
    GitHub里的Hello World!
    4 款消息队列软件产品大比拼(转)
    .net常用组件
    Dapper.NET使用(转)
    设置MYSQL允许用IP访问
    test1
    SQLServer 2008以上误操作数据库恢复方法——日志尾部备份(转)
    Quartz.NET配置
    Quartz CronTrigger配置
    Quartz CronTrigger最完整配置说明
  • 原文地址:https://www.cnblogs.com/freebird92/p/8858251.html
Copyright © 2011-2022 走看看