美文网首页Scala
Scala基础语法

Scala基础语法

作者: 羋学僧 | 来源:发表于2020-10-23 20:56 被阅读0次

    1. Scala概述

    1.1. 什么是Scala

    Scala 是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala 运行于 Java 平台(Java虚拟机),并兼容现有的 Java 程序。 Scala(Scalable Language的简称)语言是一种能够运行于 JVM 和 .Net 平台之上的通用编程语言,既可 用于大规模应用程序开发,也可用于脚本编程,它由由 Martin Odersky 于 2001 开发,2004年开始程 序运行在 JVM 与 .Net 平台之上,由于其简洁、优雅、类型安全的编程模式而受到关注。

    Scala官网

    注意三者的区别:

    面向对象编程 
    面向过程编程 
    函数式编程
    

    编程语言之分:

    1、汇编语言,脚本语言,机器语言,高级语言 
    2、静态编程语言和动态编程语言 
    3、编译型和解释型 
    4、面向对象和函数式编程 
    5、强类型语言和弱类型语言
    

    举例:

    Python是动态类型语言,是强类型语言,解释型编程语言。 
    JavaScript是动态类型语言,是弱类型语言,解释型编程语言。 
    Java是静态类型语言,是强类型语言。 
    Go是编译型语言,是静态编程语言。
    

    1.2. 为什么要学Scala

    第一,基于编程语言自身:

    优雅

    这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。
    

    速度快

    Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,所以和JRuby, Groovy比起来速度会快很多。
    

    能融合到Hadoop生态圈:

    Hadoop现在是大数据事实标准,Spark的出现并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大 部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲。
    

    Scala 和 Java 的一个对比:

    面向对象编程 面向过程编程 函数式编程

    第二,基于活跃度:

    • (1)、作为流行的开源大数据内存计算引擎Spark的源码编程语言--Spark有着良好的性能优势
    • (2)、Scala将成为未来大数据处理的主流语言

    "If I were to pick a language to use today other than Java, it would be Scala." -- James Gosling

    2. Scala编译器安装

    2.1. 安装JDK

    因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK
    安装JDK

     mkdir /root/soft/
    
     mkdir /usr/local/java/
    
    tar -zxvf /root/soft/jdk-8u73-linux-x64.tar.gz -C /usr/local/java/
    
    #配置环境变量
    vi /etc/profile
    
    ##最后添加内容
    export JAVA_HOME=/usr/local/java/jdk1.8.0_73 
    export PATH=$PATH:$JAVA_HOME/bin 
    export CLASSPATH=.:/usr/local/java/jdk1.8.0_73/lib/dt.jar:/usr/local/java/jdk1.8.0_73/lib/tools.jar
    
    ##重新加载
    source /etc/profile
    
    ##查看版本
    java -version
    

    2.2. Scala的SDK下载

    官网下载地址

    2.3. Windows平台安装Scala

    访问Scala官网下载Scala编译器安装包,目前最新版本是2.13.2,但是目 前大多数的框架都是用 2.10.x 或者 2.11.x 或者 2.12.x 编写开发的,如果将来我们要基于 Spark-2.4.5 进行学习,所以这里推荐2.12.x 版本,下载 scala-2.12.11.msi 后点击下一步就可以了,或者你也可以 基于现阶段业界主流使用的版本:scala-2.11.8

    可以不用手动配置环境变量:因为会自动配置好

    Java 全手动配置环境变量
    Python 在安装引导界面可以选择 add path 配置环境变量
    Scala 完全不用理会,会自动配置好环境变量

    2.4. Linux平台安装Scala

    访问官网的下载Scala的地址

    https://www.scala-lang.org/download/all.html
    https://www.scala-lang.org/download/2.12.11.html
    https://downloads.lightbend.com/scala/2.12.11/scala-2.12.11.tgz

    下载想要的版本,我们这里下载的是:

    scala-2.12.11.tgz

    第一步:上传到服务器

    put c:/scala-2.12.11.tgz
    

    第二步:解压缩安装到对应目录

    [root@bigdata01 ~]# mkdir /usr/local/scala 
    [root@bigdata01 ~]# tar -zxvf ~/soft/scala-2.12.11.tgz -C /usr/local/scala/
    

    第三步:配置环境变量

    vi /etc/profile
    

    添加如下内容:

    export SCALA_HOME=/usr/local/scala/scala-2.12.11
    export PATH=$PATH:$SCALA_HOME/bin
    

    执行命令使之生效

    source /etc/profile
    

    第四步:检测安装是否成功

    [root@bigdata01 ~]# scala -version 
    Scala code runner version 2.12.11 -- Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.
    

    2.5. Scala集成开发环境Intellgence IDEA安装

    目前 Scala 的开发工具主要有两种:Eclipse 和 IDEA,这两个开发工具都有相应的 Scala 插件,如果使 用Eclipse,直接到 Scala 官网下载即可

    由于 IDEA 的 Scala 插件比 Eclipse 的更优秀,所以大多数 Scala 程序员都愿意选择 IDEA,可以到社区下载免费版,点击下一步安装即可,安装时如果有网络 可以选择在线安装 Scala插件。这里我们使用离线安装 Scala 插件:

    • (1)、安装 IDEA,点击下一步即可。由于我们离线安装插件,所以点击Skip All and Set Defaul
    • (2)、下载 IEDA 的 scala 插件,地址 选择 scala 下载对应 IDEA 版本的 scala插件:scala-intellij-bin-2019.2.2.zip
    • (3)、安装插件:具体步骤如下,在启动 IDEA 以后,以此执行以下步骤 Configure --> Plugins --> Install plugin from disk --> 选择Scala插件 --> OK-->重启IDEA

    添加Scala lib



    3. Scala基础语法

    3.1. Hello Scala

    object HelloWordScala {
    /**
     * def 用来定义方法的关键字
     * main 方法名
     * args: 形参名称
     * :Unit 方法的返回值
     *
     * def max(a:Int,b :Int):Int = {}
     */
      def main(args: Array[String]): Unit = {
        println("Hello Word Scala")
      }
    }
    

    关于HelloWorld程序:Scala 和 Java 的对比不同处:

    1、类名和文件名
    2、main方法的定义(注意和java的main方法对比理解)
    3、分号(可有可无)
    4、编译和执行(scalac和scala)
    5、类的声明(object和class)

    3.2. 变量的定义

    object VariableDemo {
        
        def main(args: Array[String]) {
            
            //使用val定义的变量值是不可变的,相当于java里用final修饰的变量
            val i = 1
            //使用var定义的变量是可变得,在Scala中鼓励使用val
            val s = "hello"
            //Scala编译器会自动推断变量的类型,必要的时候可以指定类型
            //变量名在前,类型在后
            val str: String = "你好!世界!"
            println(i, s, str)
            
            /**
             * val 相当于Java中的final关键字修饰的变量,一旦赋值便不能更改
             * 当未指定类型时,Scala会根据实际类型进行类型推断,上面前三种方式结果相同
             * lazy关键字修饰的变量,定义时不赋值,真正使用时才赋值
             * var关键字修饰的变量,可以被重新赋值
             * e也可以是大写E,0.1214e1 = 0.1314*10
             * e也可以是大写E,0.1214e2 = 0.1314*10*10
             */
            
            // 定义变量
            val hello1 = "Hello Scala"
            val hello2: String = "Hello Scala"
            val hello3: java.lang.String = "Hello Scala"
            lazy val hello4 = "Hello Scala"
            
            // 使用val修饰的变量是不可更改变量。相当于java中的final关键字修饰的变量
            // 这个赋值操作是不被允许的: hello5 = "hello spark"
            val hello5 = "Hello Scala"
            
            
            // 使用var修饰的变量可以修改为同类型的其他值, 但是使用val修饰的变量一定不能修改
            var hello6 = "hello"
            // 这个赋值操作是错误的: hello6 = 4
            hello6 = "hi" // 这个操作是允许的
            
            val x1 = 0x29 //十六进制定义整数
            val x2 = 41 //十进制定义整数
            val x3 = 51 //八进制数定义
            println(x1, x2)
            
            val doubleNumber = 3.14159 //Double类型定义,直接输入浮点数,编译器会将其自动推断为Double类型
            val floatNumber1 = 3.14159F //定义Float类型浮点数,需要在浮点数后面加F或f
            val floatNumber2 = 0.1314e-1 // 科学计数法
            println(doubleNumber, floatNumber1, floatNumber2)
            
            // 字符定义,用单引号(')将字符包裹
            var letter = 'A'
            // 如果需要原样输出字符串中的内容,则用三个双引号"""将字符串包裹起来
            val hello = """Hello  \n \t \b \\ Scala"""
            // 定义布尔变量
            var bool = true
            println(letter, hello, bool)
        }
    }
    
    

    总结:

    1、数据类型可以指定,也可以不指定,如果不指定,那么就会进行数据类型的自动推断。
    2、如果指定数据类型,数据类型的执行方式是在变量名后面写一个冒号,然后写上数据类型。
    3、我们的scala里面变量的修饰符一共有两个,一个是var,一个是val。如果是var修饰的变量,那么这个 变量的值是可以修改的,虽然可以修改,但是不能改成其他类型的值。如果是val修饰的变量,那么这个变量 的值是不可以修改的

    懒加载、延迟加载(lazy),两个例子:

    scala> import scala.io.Source 
    import scala.io.Source
    
    scala> lazy val file = Source.fromFile("/home/bigdata/student.txt") 
    file: scala.io.BufferedSource = <lazy>
    
    scala> file 
    res2: scala.io.BufferedSource = non-empty iterator
    
    scala> for (f <- file.getLines) println(f) 
    95002,刘晨,女,19,IS 
    95017,王风娟,女,18,IS 
    95018,王一,女,19,IS 
    95013,冯伟,男,21,CS 
    95014,王小丽,女,19,CS 
    95019,邢小丽,女,19,IS 
    95020,赵钱,男,21,IS 
    95003,王敏,女,22,MA 
    95004,张立,男,19,IS 
    95012,孙花,女,20,CS 
    95010,孔小涛,男,19,CS 
    95005,刘刚,男,18,MA 
    95006,孙庆,男,23,CS 
    95007,易思玲,女,19,MA 
    95008,李娜,女,18,CS 
    95021,周二,男,17,MA 
    95022,郑明,男,20,MA 
    95001,李勇,男,20,CS 
    95011,包小柏,男,18,MA 
    95009,梦圆圆,女,18,MA 
    95015,王君,男,18,MA
    

    4. 数据类型

    4.1. 数据类型概述

    Scala 和 Java 一样,有7种数值类型 Byte、Char、Short、Int、Long、Float 和 Double(无包装类 型)和一个Boolean 类型,再加上常用的String类型。

    数据类型

    注意:scala里面没有基本数据类型和包装类型之说。如果大家非要说的话,那么大家都可以认为都有 的类型的类型都是包装类型。


    一些代码测试:

    /**
     * 描述: 数据类型
     *
     * 要点:
     * 1、Any是所有类的父类,包括值类型AnyVal,和引用类型AnyRef
     * 2、AnyVal是所有值类型的父类,包括Int,Double,Boolean,Unit等等
     * 3、AnyRef是所有引用类型的父类,包括Null
     * 4、Null是所有引用类型的子类
     * 5、Nothing是所有类的子类
     * 6、Unit类型只有一个实例,是(),相当于java中的void,没有任何的实质意义
     * 7、Null也只有一个实例,是null,相当于java中的null,能赋值给任何引用类型变量,不能赋值给值类型变量
     */
    object TypeDemo {
        
        def main(args: Array[String]): Unit = {
            
            val var_int = 1
            val var_double = 3.33
            val var_float = 2.4F
            val var_char = 'A'
            val var_bool = true
            println(var_int, var_double, var_float, var_char, var_bool)
            
            val var_16 = 0x29
            val var_string = "aa"
            val var_string1 = "\"huangbo\""
            val var_string2 = """hello\thello\na\t\\"""
            
            println(var_16)
            println(var_string)
            println(var_string1)
            println(var_string2)
            
        }
    }
    
    

    Scala的数据类型的结构图和关系:

    总结:

    1、Any是所有类的父类,包括值类型AnyVal,和引用类型AnyRef
    2、AnyVal是所有值类型的父类,包括Int,Double,Boolean,Unit等等
    3、AnyRef是所有引用类型的父类,包括Null
    4、Null是所有引用类型的子类
    5、Nothing是所有类的子类
    6、Unit类型只有一个实例,是(),相当于java中的void,没有任何的实质意义
    7、Null也只有一个实例,是null,相当于java中的null,能赋值给任何引用类型变量,不能赋值给值类型 变量

    4.2. Scala基本类型操作

    算术操作

    object Test {
       def main(args: Array[String]): Unit ={
          var a = 10;
          var b = 20;
          var c = 25;
          var d = 25;
          println("a + b = " + (a + b) );
          println("a - b = " + (a - b) );
          println("a * b = " + (a * b) );
          println("b / a = " + (b / a) );
          println("b % a = " + (b % a) );
          println("c % a = " + (c % a) );
         
       }
    }
    

    关系运算

    object Test {
       def main(args: Array[String]): Unit ={
          var a = 10;
          var b = 20;
          println("a == b = " + (a == b) );
          println("a != b = " + (a != b) );
          println("a > b = " + (a > b) );
          println("a < b = " + (a < b) );
          println("b >= a = " + (b >= a) );
          println("b <= a = " + (b <= a) );
       }
    }
    

    逻辑运算

    object Test {
       def main(args: Array[String]): Unit = {
          var a = true;
          var b = false;
    
          println("a && b = " + (a&&b) );
    
          println("a || b = " + (a||b) );
    
          println("!(a && b) = " + !(a && b) );
       }
    }
    

    位运算

    object Test {
       def main(args: Array[String]): Unit =  {
          var a = 60;           /* 60 = 0011 1100 */  
          var b = 13;           /* 13 = 0000 1101 */
          var c = 0;
    
          c = a & b;            /* 12 = 0000 1100 */
          println("a & b = " + c );
    
          c = a | b;            /* 61 = 0011 1101 */
          println("a | b = " + c );
    
          c = a ^ b;            /* 49 = 0011 0001 */
          println("a ^ b = " + c );
    
          c = ~a;               /* -61 = 1100 0011 */
          println("~a = " + c );
    
          c = a << 2;           /* 240 = 1111 0000 */
          println("a << 2 = " + c );
    
          c = a >> 2;           /* 15 = 1111 */
          println("a >> 2  = " + c );
    
          c = a >>> 2;          /* 15 = 0000 1111 */
          println("a >>> 2 = " + c );
       }
    }
    

    赋值运算符

    object Test {
       def main(args: Array[String]): Unit =  {
          var a = 10;      
          var b = 20;
          var c = 0;
    
          c = a + b;
          println("c = a + b  = " + c );
    
          c += a ;
          println("c += a  = " + c );
    
          c -= a ;
          println("c -= a = " + c );
    
          c *= a ;
          println("c *= a = " + c );
    
          a = 10;
          c = 15;
          c /= a ;
          println("c /= a  = " + c );
    
          a = 10;
          c = 15;
          c %= a ;
          println("c %= a  = " + c );
    
          c <<= 2 ;
          println("c <<= 2  = " + c );
    
          c >>= 2 ;
          println("c >>= 2  = " + c );
    
          c >>= a ;
          println("c >>= a  = " + c );
    
          c &= a ;
          println("c &= 2  = " + c );
         
          c ^= a ;
          println("c ^= a  = " + c );
    
          c |= a ;
          println("c |= a  = " + c );
       }
    }
    

    4.3. 编码规范

    1、分号:在scala编码中,不强制在代码末尾加分号,但是如果有多句代码写在同一行,那么必须使用分号 进行隔开
    2、注释:在scala编程中,注释的方式和Java中注释方式一样,原则:少而精

    object HelloWorld {
       /* 这是一个 Scala 程序
        * 这是一行注释
        * 这里演示了多行注释
        */
       def main(args: Array[String]): Unit =  {
          // 输出 Hello World
          // 这是一个单行注释
          println("Hello, world!") 
       }
    }
    

    3、变量命名:和Java的编码规范一致,采用驼峰法则
    4、关键字:关注新关键字:yield, match, object, def, implicit, trait, sealed, var/val

    5. 流程控制

    5.1. if

    Scala的的条件表达式比较简洁,例如:

    object IfDemo {
        
        def main(args: Array[String]) {
            
            // if条件分支是可以拥有返回值的。!!返回值为代码段中的最后一个局代码的返回结果
            var aa = 1
            var result = if (aa > 0) {
                println(">")
            } else {
                println("<")
            }
            println(result)
            
            val x = 1
            //判断x的值,将结果赋给y
            val y = if (x > 0) 1 else -1
            //打印y的值
            println(y)
            
            //支持混合类型表达式
            val z = if (x > 1) 1 else "error"
            //打印z的值
            println(z)
            
            //如果缺失else,相当于if (x > 2) 1 else ()
            val m = if (x > 2) 1
            println(m)
            
            //在scala中每个表达式都有值,scala中有个Unit类,写做(),相当于Java中的void
            val n = if (x > 2) 1 else ()
            println(n)
            
            //if和else if
            val k = if (x < 0) -1 else if (x > 0) 1 else 0
            println(k)
        }
    }
    
    

    总结:

    1)if条件表达式它是有返回值的,返回值是多个分支的返回结果的共同父类
    2)返回值会根据条件表达式的情况会进行自动的数据类型的推断(返回的是多个分支的共同父类)

    5.2. 块表达式

    object BlockExpresstionDemo {
        
        def main(args: Array[String]) {
            val x = 0
            //在scala中{}中课包含一系列表达式,块中最后一个表达式的值就是块的值
            //下面就是一个块表达式
            val result = {
                if (x < 0) {
                    -1
                } else if (x >= 1) {
                    1
                } else {
                    "error"
                }
            }
            //result的值就是块表达式的结果
            println(result)
            
            //返回块中的最后一个表达式的值
            val aa = {
                1 to 10; if (3 > 2) 3 else 2
            }
            println(aa)
            
            val bb = {
                1 to 10; if (3 > 2) 3 else 2; val cc = 2
            }
            println(bb)
        }
    }
    

    总结:

    就算是赋值表达式,也是有返回值的。是空,是Unit

    scala> val a = {val bb = 2} 
    a: Unit = ()
    
    scala> val a = {val bb = 2; 2} 
    a: Int = 2
    

    5.3. for

    for的语法结构:

    for循环语法结构:for (i <- 表达式/数组/集合)

    代码:

    object ForDemo {
        
        def main(args: Array[String]) {
            //for(i <- 表达式),表达式1 to 10返回一个Range(区间)
            //每次循环将区间中的一个值赋给i
            for (i <- 1 to 10)
                println(i)
            
            //for(i <- 数组)
            val arr = Array("a", "b", "c")
            for (i <- arr)
                println(i)
            
            // 倒序打印
            for (str <- arr.reverse) {
                println(str)
            }
            
            // 使用数组下标的方式进行打印
            for (i <- 0 to arr.length - 1) {
                println(arr(i))
            }
            
            for (i <- 0 until arr.length)
                println(arr(i))
            
            println("-----------------")
            for (i <- 0 until(arr.length, 2))
                println(arr(i))
            
            //高级for循环
            //每个生成器都可以带一个条件,注意:if前面没有分号
           //守卫条件:if i != j
            for (i <- 1 to 3; j <- 1 to 3 if i != j)
                println((10 * i + j) + " ")
            println()
            
            //for推导式:如果for循环的循环体以yield开始,则该循环会构建出一个集合
            //每次迭代生成集合中的一个值
            val v = for (i <- 1 to 10) yield i * 10
            println(v)
            
        }
    }
    

    总结:

    1、在scala里面没有运算符,都有的符号其实都是方法。
    1 to 10 1.to(10) a.f(b)
    2、在scala里面没有 ++ -- 的用法
    3、for(i <- 表达式/数组/集合)
    4、在for循环里面我们是可以添加if表达式 (守卫条件)
    5、有两个特殊表达式需要了解:
    To 1 to 3 1 2 3
    To 1 to (3,2) 1 3
    start to (end, step)
    Until 1 until 3 1 2
    6、如果在使用for循环的时候,for循环的时候我们需要获取,我们可以是使用yield关键字

    5.4. while

    代码:

    object WhileDemo {
        def main(args: Array[String]) {
            
            // Scala中的while的语法和java中的一样
            //定义起始条件
            var n = 10;
            // ( ) 中是终止条件
            while (n > 0) {
                println(n)
                
                //迭代条件
                n -= 1
            }
        }
    }
    

    总结:

    1、while使用跟java一模一样 
    2、注意点:在scala里面不支持 i++  i-- 等操作统一写成 i+=1  i-=1
    

    5.5. break和continue

    Scala 里面竟然没有 break 和 continue 关键字,其实不是这样的,Scala 里面推荐使用函数式的风格解 决 break 和 continue 的功能,而不是一个关键字。
    break:跳出整个循环, continue:跳出当前循环

    import util.control.Breaks._
    
    object Break_ContinueDemo {
        
        def main(args: Array[String]): Unit = {
            
            // break 举例
            breakable(
                for (i <- 1 to 5) {
                    if (i == 3) {
                        break()
                    }
                    println(i)
                }
            )
            println("----------------")
            // continue 举例
            for (i <- 1 to 5) {
                breakable {
                    if (i == 3) {
                        break
                    }
                    println(i)
                }
            }
            
        }
    }
    
    

    6. 方法和函数

    Scala 中的 + - * / % 等操作符的作用与 Java 一样,位操作符 & | ^ >> << 也一样。 
    只是有一点特别的:这些操作符实际上是方法。例如: 
    a + b 是如下方法调用的简写: 
    a.+(b)
    "a 方法 b" 可以写成 "a.方法(b)"
    

    6.1. 定义方法

    方法的返回值类型可以不写,编译器可以自动推断出来,但是对于递归函数,必须指定返回类型 注意:函数体应该改成叫方法体!如果不写等号,代表没有返回值。

    scala> def m2(x:Int, y:Int):Int = x + y m2: (x: Int, y: Int)Int
    scala> def m2(x:Int, y:Int){println("aa"); var aa=2; aa} m2: (x: Int, y: Int)Unit
    
    object MethodDemo {
        
        def main(args: Array[String]): Unit = {
            println(max(2, 3))
            println(sub(4, 3))
            println(sub1(4, 3))
            
            println(decorate("Hello", "<<<", ">>>")) // <<<Hello>>>
            println(decorate("Hello", "<<<")) // <<<Hello]
            println(decorate(left = "<<<", str = "Hello", right = ">>>")) // 指定参数名,<<<Hello>>>
            println(decorate("Hello", right = ">>>")) // [Hello>>>
            
            println(sum(1, 2, 3, 4, 5))
            println(sum(4, 5, 6, 7, 8, 9))
            
            println(myPrint("huangbo"))
        }
        
        /**
         * def:关键字,定义一个函数
         * max:自定义的方法名
         * (x:Int, y:Int):方法名后小扩号中为参数列表
         * Int:参数后的Int为方法返回值类型
         * {…}:大扩号中为方法体
         * =:如果没有等号, 表示该方法的返回值是Unit
         *
         * Scala函数返回值可以不加return,默认函数体最后一条语句为返回值
         * 函数体不指定返回值时,scala会根据实际类型进行类型推断
         * Unit关键字表示函数不存在返回值,相当于java中的void关键字
         * Scala每行语句结束后的分号可加可不加
         */
        def max(x: Int, y: Int): Int = {
            if (x > y) {
                x
            }
            else {
                y
            }
        }
        
        // 如果没有return语句,那么方法中的最后一行就是返回值
        def sub(a: Int, b: Int) = {
            a - b
        }
        
        // 方法中的最后一行就是返回值,也可以使用return显示声明这就是返回值。
        // 由于方法的定义中声明了,返回值就是Unit,所以sub1方法的返回值始终就是(),就算使用return进行返回也无济于事
        def sub1(a: Int, b: Int): Unit = {
            return a - b
        }
        
        // 如果定义一个方法,不需要返回值,那么可以省略 "="
        def myPrint(s: String) {
            println(s)
        }
        
        def myPrint1(s: String): Unit = {
            println(s)
        }
        
        // 定义带默认参数的方法
        def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
        
        // 定义变长参数方法
        def sum(args: Int*): Int = {
            var result = 0
            for (arg <- args) result += arg
            result
        }
    }
    

    6.2. 定义函数

    object FunctionDemo {
        
        //定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
        val f1 = (x: Int, y: Int) => x + y
        
        //再定义一个函数f2
        val f2 = (m: Int, n: Int) => m * n
        
        val f3 = (x: Int) => x
        
        // 函数其实可以简化(正确方式1)
        val f4 = 1 + (_: Int)
        // 函数其实可以简化(正确方式2)
        val f5: Int => Int = 1 + _
        
        def main(args: Array[String]): Unit = {
            
            println(f1(2, 3))
            println(f2(2, 3))
            println(f3(3))
            println(f4(4))
            println(f5(4))
        }
    }
    

    函数的意义

    表示接受两个Int类型的变量,然后做累加。 经过 scala 的自动类型推断得知,最后返回的结果数据的类型也是 Int。 Function2 中 2 表示这个函数接收的参数个数是2个。

    6.3. 方法和函数的区别

    1、函数可以作为参数传递给方法,也就是说函数可以作为方法的参数。在函数式编程语言中,函数是 “头等公民”,它可以像任何其他数据类型一样被传递和操作。案例:首先定义一个方法,再定义一个函 数,然后将函数传递到方法里面
    2、函数可以作为方法的参数,但是也可以作为函数的参数,例如:
    3、方法也可以作为方法的参数。在需要传入函数作为参数的位置上传入一个方法的话,那么这个方法 会被自动的转换为函数作为参数,也可以通过“_”把方法转换为参数
    4、方法也可以作为函数的参数。其实,原理就是方法会被自动转换为函数,所以也就是传入一个函数 到一个函数作为参数。
    5、综合测试

    object MethodAndFunctionDemo {
        
        //方法m1参数要求是一个函数,函数的参数必须是两个Int类型,返回值类型也是Int类型
        def m1(f: (Int, Int) => Int): Int = f(2, 6)
        
        // 定义一个需要两个Int类型参数的方法
        def m2(x: Int, y: Int): Int = x + y
        
        // 定义一个计算数据不被写死的方法
        def m3(f: (Int, Int) => Int, x: Int, y: Int): Int = f(x, y)
        
        // 定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
        val f1 = (x: Int, y: Int) => x + y
        // 再定义一个函数f2
        val f2 = (m: Int, n: Int) => m * n
        // 定义一个传入函数的函数
        val f3 = (f: (Int, Int) => Int, x: Int, y: Int) => f(x, y)
        
        //main方法
        def main(args: Array[String]) {
            
            //调用m1方法,并传入f1函数
            val r1 = m1(f1)
            println(r1)
            
            //调用m1方法,并传入f2函数
            val r2 = m1(f2)
            println(r2)
            
            // 调用m3方法, 传入f1函数
            val result1 = m3(f1, 2, 4)
            println(result1)
            
            // 调用m3方法,传入f2函数
            val result2 = m3(f2, 2, 4)
            println(result2)
            
            // 调用m3方法, 传入m2方法作为参数
            println(m3(m2, 2, 4))
            
            // 调用f3函数, 传入f1函数
            println(f3(f1, 3, 4))
        }
    }
    

    6.4. 将方法转换成函数使用

    6.5. Scala函数式编程特点

    (1)高阶函数(Higher-order functions)
    (2)闭包(closures)
    (3)模式匹配(Pattern matching)
    (4)单一赋值(Single assignment)
    (5)延迟计算(Lazy evaluation)
    (6)类型推导(Type inference)
    (7)尾部调用优化(Tail call optimization)

    7. Scala数组Array

    String
    StringBuffer

    7.1. 定长数组和变长数组

    Array
             array.toBuffer()
    ArrayBuffer        
             arrayBuffer.toArray()   
    

    1、由于Array是不可变(长度不可变)的,初始化之初就有了固定的长度,所以不能直接地对其元素进行删 除操作,也不能多增加元素,只能修改某个位置的元素的值,要实现删除可以通过过滤生成新的Array的方式 来删除不要的元素。所以也就没有add,insert,remove等操作。
    2、而ArrayBuffer是可变的,本身提供了很多元素的操作,当然包括增加,删除操作。
    3、如果你需要在Array和ArrayBuffer之间转换,那么分别调用toBuffer()和toArray()方法即可

    object ArrayDemo {
        
        def main(args: Array[String]) {
            
            //初始化一个长度为8的定长数组,其所有元素均为0
            val arr1 = new Array[Int](8)
            //直接打印定长数组,内容为数组的hashcode值
            println(arr1)
            
            //将数组转换成数组缓冲,就可以看到原数组中的内容了
            //toBuffer会将数组转换长数组缓冲
            println(arr1.toBuffer)
            
            //注意:如果new,相当于调用了数组的apply方法,直接为数组赋值
            //初始化一个长度为1的定长数组
            val arr2 = Array[Int](10)
            println(arr2.toBuffer)
            
            //定义一个长度为3的定长数组
            val arr3 = Array("hadoop", "storm", "spark")
            //使用()来访问元素
            println(arr3(2))
            arr3(2) = "hive"
            println(arr3.toBuffer)
            println("-----------------------------------------------------------")
            
            //////////////////////////////////////////////////
            //变长数组(数组缓冲)
            //如果想使用数组缓冲,需要导入import scala.collection.mutable.ArrayBuffer包
            val ab = new ArrayBuffer[Int]()
            //向数组缓冲的尾部追加一个元素
            //+=尾部追加元素
            ab += 1
            //追加多个元素
            ab += (2, 3, 4, 5)
            //追加一个数组++=
            ab ++= Array(6, 7, 7, 7)
            //追加一个数组缓冲
            ab ++= ArrayBuffer(8, 9)
            // 追加一个List
            ab ++= List(10, 11)
            //打印数组缓冲ab
            println(ab)
            
            //在数组某个位置插入元素用insert
            ab.insert(0, -1, 0)
            //删除数组某个位置的元素用remove
            ab.remove(8, 2)
            
            println(ab)
        }
    }
    

    7.2. 遍历数组

    1、增强for循环
    2、使用to可以生成序列,0 to 10 包含0包含10
    3、好用的until会生成脚标,0 until 10 包含0不包含10

    object ArrayForDemo {
        
        def main(args: Array[String]) {
            //初始化一个数组
            val arr = Array(1, 2, 3, 4, 5, 6, 7, 8)
            
            //增强for循环
            for (i <- arr) {
                println(i)
            }
            
            //使用to可以生成一个序列作为脚标
            for (i <- (0 to arr.length - 1).reverse)
                println(arr(i))
            
            //好用的until会生成一个Range,reverse是将前面生成的Range反转
            for (i <- (0 until arr.length).reverse)
                println(arr(i))
            
            //步长为2
            for (i <- (0 until(arr.length, 2)).reverse)
                println(arr(i))
        }
    }
    

    7.3. 数组转换

    yield关键字将原始的数组进行转换会产生一个新的数组,原始的数组不变

    object ArrayYieldDemo {
        
        def main(args: Array[String]) {
            
            //定义一个数组
            val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
            //将偶数取出乘以10后再生成一个新的数组
            val res = for (e <- arr if e % 2 == 0) yield e * 10
            println(res.toBuffer)
            
            //更高级的写法,用着更爽
            //filter是过滤,接收一个返回值为boolean的函数
            //map相当于将数组中的每一个元素取出来,应用传进去的函数
            val r = arr.filter(_ % 2 == 0).map(_ * 10)
            println(r.toBuffer)
            
        }
    }
    

    7.4. 数组常用算法

    在Scala中,数组上的某些方法对数组进行相应的操作非常方便!

    object ArrayTestDemo {
        
        def main(args: Array[String]): Unit = {
            
            val array = Array(4, 2, 7, 8, 1, 2, 2, 8, 8, 9)
            
            val ab: ArrayBuffer[Int] = new ArrayBuffer[Int]()
            ab += (4, 3, 5, 6, 2, 7, 1, 8)
            
            val result11 = array.reduce((x: Int, y: Int) => x + y)
            println(result11)
            
            println(array.length)
            println(array.max)
            println(array.min)
            println(array.sum)
            println(array.mkString("-"))
            println(array.mkString("<", ",", ">"))
            println(array.filter((x: Int) => if (x % 2 == 0) true else false).toBuffer)
            ab.trimStart(2)
            println(ab)
            ab.trimEnd(2)
            println(ab)
            println("-----------------1-----------------")
            
            /**
             * array的map和reduce操作
             */
            val newArray: Array[Int] = array.map((x: Int) => x * 2)
            println(newArray.toBuffer)
            val newArray2: Int = array.reduce((x: Int, y: Int) => x + y)
            println(newArray2)
            println("-----------------2----------------")
            
            /**
             * fold操作
             */
            array.foldLeft[Int](0)((init, value) => {
                println(init, value);
                init + value
            })
            println("------------------3----------------")
            array.foldRight[Int](0)((init, value) => {
                println(init, value);
                init + value
            })
            val result: Int = array.fold(0)((x: Int, y: Int) => x + y)
            println(result)
            
            /**
             * array求平均值
             */
            val result2: (Int, Int) = array.map((x: Int) => (x, 1))
              .reduce((x: (Int, Int), y: (Int, Int)) => (x._1 + y._1, x._2 + y._2))
            println(result2._1 / result2._2.toDouble)
            println("-----------------4-----------------")
            
            /**
             * 排序
             */
            println(array.sorted.toBuffer)
            println(array.sortBy(x => x).reverse.toBuffer)
            println(array.sortWith((x: Int, y: Int) => x > y).toBuffer)
            Sorting.quickSort(array)
            println(array.toBuffer)
        }
    }
    
    

    7.5. 多维数组

    8. Scala集合相关

    8.1. Scala集合-概述

    Scala的集合有三大类:序列Seq、集合Set、映射Map,所有的集合都扩展自Iterable特质 在Scala中集合有可变(mutable)和不可变(immutable)两种类型,immutable类型的集合初始化 后就不能改变了(注意与val修饰的变量进行区别) 官网解释

    Scala collections systematically distinguish between mutable and immutable collections. A mutable collection can be updated or extended in place. This means you can change, add, or remove elements of a collection as a side effect. Immutable collections, by contrast, never change. You have still operations that simulate additions, removals, or updates, but those operations will in each case return a new collection and leave the old collection unchanged.

    大致意思是:Scala中的集合分为两种,一种是可变的集合,另一种是不可变的集合 可变的集合可以更新或修改,添加、删除、修改元素将作用于原集合 不可变集合一量被创建,便不能被改变,添加、删除、更新操作返回的是新的集合,老集合保持不变

    scala集合类的层次结构:

    scala.collection包中的集合类层次结构如下图:

    These are all high-level abstract classes or traits, which generally have mutable as well as immutable implementations.

    scala.collection.immutable包中的类层次结构:

    scala.collection.mutable包中的类层次结构:

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

    List                        序列                 类似于Java中的List,元素有序,可以重复
    Set                         集合                 类似于Java中的Set,元素无序,不重复
    Map                         映射                 类似于Java中的Map,元素是key-value形式
    Tuple                       元组                 类似于Java中的一个 自定义POJO的简写
    

    val和var:表明定义的变量(引用)是否能被修改而指向其他内容 immutable和mutable:表明的是内存中开辟出来的这块空间里的内容能否被修改,如果针对immutable变 量进行修改,其实是开辟了一块新的内存空间,产生了一个新的变量,而原来的变量依然没有改变

    8.2. Scala序列--List

    不可变的序列:import scala.collection.immutable._

    在Scala中列表要么为空(Nil表示空列表)要么是一个head元素加上一个tail列表。
    9 :: List(5, 2) :: 操作符是将给定的头和尾创建一个新的列表
    注意::: 操作符是右结合的,如9 :: 5 :: 2 :: Nil相当于 9 :: (5 :: (2 :: Nil))

    object ImmutableListDemo {
        
        def main(args: Array[String]) {
            //创建一个不可变的集合
            val list1 = List(1, 2, 3)
            
            //将0插入到lst1的前面生成一个新的List
            val list2 = 0 :: list1
            val list3 = list1.::(0)
            val list4 = 0 +: list1
            val list5 = list1.+:(0)
            println(list2, list3, list4, list5)
            
            //将一个元素添加到lst1的后面产生一个新的集合
            val list6 = list1 :+ 3
            println(list6)
            
            val list0 = List(4, 5, 6)
            //将2个list合并成一个新的List
            val list7 = list1 ++ list0
            //将lst0插入到lst1前面生成一个新的集合
            val list8 = list1 ++: list0
            //将lst0插入到lst1前面生成一个新的集合
            val list9 = list1.:::(list0)
            println(list7)
            println(list8)
            println(list9)
        }
    }
    

    List的常用方法
    //采用::及Nil进行列表构建
    scala> val nums = 1 :: (2 :: (3 :: (4 :: Nil))) 
    nums: List[Int] = List(1, 2, 3, 4)
    
    //由于::操作符的优先级是从右往左的,因此上一条语句等同于下面这条语句 
    scala> val nums=1::2::3::4::Nil 
    nums: List[Int] = List(1, 2, 3, 4)
    
    //判断是否为空 
    scala> nums.isEmpty 
    res108: Boolean = false
    
    //取第一个无素 
    scala> nums.head 
    res109: Int = 1
    
    //取除第一个元素外剩余的元素,返回的是列表 
    scala> nums.tail 
    res114: List[Int] = List(2, 3, 4)
    
    //取列表第二个元素 
    scala> nums.tail.head 
    res115: Int = 2
    
    //List连接操作 
    scala> List(1,2,3):::List(4,5,6) 
    res116: List[Int] = List(1, 2, 3, 4, 5, 6)
    
    //取除最后一个元素外的元素,返回的是列表 
    scala> nums.init 
    res117: List[Int] = List(1, 2, 3)
    
    //取列表最后一个元素 
    scala> nums.last 
    res118: Int = 4
    
    //列表元素倒置 
    scala> nums.reverse 
    res119: List[Int] = List(4, 3, 2, 1)
    
    //一些好玩的方法调用 
    scala> nums.reverse.reverse==nums 
    res120: Boolean = true
    
    scala> nums.reverse.init 
    res121: List[Int] = List(4, 3, 2)
    
    scala> nums.tail.reverse 
    res122: List[Int] = List(4, 3, 2)
    
    //丢弃前n个元素 
    scala> nums drop 3 
    res123: List[Int] = List(4)
    
    scala> nums drop 1 
    res124: List[Int] = List(2, 3, 4)
    
    //获取前n个元素 
    scala> nums take 1
    res125: List[Int] = List(1)
    
    scala> nums.take(3) 
    res126: List[Int] = List(1, 2, 3)
    
    //将列表进行分割 
    scala> nums.splitAt(2) 
    res127: (List[Int], List[Int]) = (List(1, 2),List(3, 4))
    
    //前一个操作与下列语句等同 
    scala> (nums.take(2),nums.drop(2)) 
    res128: (List[Int], List[Int]) = (List(1, 2),List(3, 4))
    
    //Zip操作,返回的是List类型的元组(Tuple) 
    scala> val nums=List(1,2,3,4) 
    nums: List[Int] = List(1, 2, 3, 4)
    
    scala> val chars=List('1','2','3','4')
    chars: List[Char] = List(1, 2, 3, 4)
    
    scala> nums zip chars 
    res130: List[(Int, Char)] = List((1,1), (2,2), (3,3), (4,4))
    
    //List toString方法 
    scala> nums.toString 
    res131: String = List(1, 2, 3, 4)
    
    //List mkString方法 
    scala> nums.mkString res132: String = 1234
    
    //转换成数组 
    scala> nums.toArray 
    res134: Array[Int] = Array(1, 2, 3, 4)
    
    

    可变的序列:import scala.collection.mutable._

    object MutableList Demoextends App {
        //构建一个可变列表,初始有3个元素1,2,3
        val list0 = ListBuffer[Int](1, 2, 3)
        //创建一个空的可变列表
        val list1 = new ListBuffer[Int]
        //向list1中追加元素,注意:没有生成新的集合
        list1 += 4
        list1.append(5)
        println(list1)
        
        //将list1中的元素最近到list0中, 注意:没有生成新的集合
        list0 ++= list1
        
        //将list0和list1合并成一个新的ListBuffer 注意:生成了一个集合
        val list2 = list0 ++ list1
        
        //将元素追加到list0的后面生成一个新的集合
        val list3 = list0 :+ 5
    }
    
    

    8.3. Scala集合--Set

    不可变的Set
    import scala.collection.immutable.HashSet

    object ImmutableSetDemo extends App {
        
        val set1 = new HashSet[Int]()
        //将元素和set1合并生成一个新的set,原有set不变
        val set2 = set1 + 4
        
        //set中元素不能重复
        val set3 = set1 ++ Set(5, 6, 7)
        
        val set0 = Set(1, 3, 4) ++ set1
        println(set0.getClass)
    }
    

    可变的Set
    import scala.collection.mutable

    object MutableSetDemo extends App {
        
        //创建一个可变的HashSet
        val set1 = new mutable.HashSet[Int]()
        
        //向HashSet中添加元素
        set1 += 2
        //add等价于+=
        set1.add(4)
        
        set1 ++= Set(1, 3, 5)
        println(set1)
        
        //删除一个元素
        set1 -= 5
        set1.remove(2)
        println(set1)
    }
    

    怎么求集合的交集并集差集呢?

    object SetTestDemo {
        
        def main(args: Array[String]): Unit = {
            
            val set1: Set[Int] = Set(1, 2, 3, 4, 5)
            val set2: Set[Int] = Set(3, 4, 5, 6, 7)
            
            println(set1.union(set2))
            println(set1.intersect(set2))
            println(set1.diff(set2), set2.diff(set1))
        }
    }
    
    

    8.4. Scala集合--Map

    object MutableMapDemo extends App {
        
        val map1 = new mutable.HashMap[String, Int]()
        
        //向map中添加数据
        map1("spark") = 1
        map1 += (("hadoop", 2))
        map1.put("storm", 3)
        println(map1)
        
        //从map中移除元素
        map1 -= "spark"
        map1.remove("hadoop")
        println(map1)
        println("----------------------1--------------------------------------------")
        
        // 访问map1中的key-value
        println(map1("storm"))
        //  println(map1("spark"))
        val result = if (map1.contains("spark")) map1("spark") else 0
        println(result)
        println(map1.get("storm"))
        println(map1.getOrElse("storm", 0))
        println(map1.getOrElse("spark", 0))
        println("----------------------2--------------------------------------------")
        
        // 遍历
        val map2 = Map("a" -> 1, "b" -> 2, "c" -> 3)
        for (kv <- map2) {
            println(kv._1, kv._2)
        }
        for ((k, v) <- map2) {
            println(k, v)
        }
        println("----------------------3--------------------------------------------")
    }
    

    8.5. Scala元组--Tuple

    映射是K/V对偶的集合,对偶是元组的最简单形式,元组可以装着多个不同类型的值。

    8.5.1. 创建元组

    8.5.2. 获取元组中的值

    8.5.3. 将对偶的元组转成集合

    其实对于二元组集合,完全可以认为是一个map集合,所以二元组集合可以和map进行相互转换。

    8.5.4. 元组拉链操作

    zip命令可以将多个值绑定在一起

    注意:如果两个数组的元素个数不一致,拉链操作后生成的数组的长度为较小的那个数组的元素个数 相反操作:unzip

    object TupleDemo {
       
       def main(args: Array[String]): Unit = {
           
           // 声明一个Tuple
           val t = ("huangbo", 22, 55.55, true)
           
           // 声明一个Tuple
           val t1, (a, b, c, d) = ("huangbo", 22, 55.55, true)
           
           // 打印输出一个Tuple
           println(t1)
           println(a, b, c, d)
           
           // 访问Tuple对应位置上的元素
           val aa = t1._1
           println(aa, t1._2, t1._3)
           
           // 将元素是 二元组 的集合转换成Map
           var array = Array(("A", 1), ("B", 2))
           val map1 = array.toMap
           println(map1)
           
           // 可以将一个map转成一个 二元组 的数组
           val tuple1 = map1.toArray
           println(tuple1.toBuffer)
           
           // 如果只需要使用元组中的部分元素呢。
           val (aa1, _, bb1, _) = t1
           println(aa1, bb1)
           
           // List的拉链操作
           val alist = List("a", "b", "c")
           val blist = List(1, 2, 3)
           val result33: Map[String, Int] = alist.zip(blist).toMap
           println(result33)
       }
    }
    
    

    8.6. Scala映射—Map

    在Scala中,把哈希表这种数据结构叫做映射,在Java中也叫做映射
    在Python中,把哈希表这种数据结构叫做字典
    不管叫什么,存储的都是key-value对形式的键值对

    8.6.1. 构建Map

    scala> val scores = Map("huangbo" -> 85, "xuzheng" -> 99, "huanglei" -> 90) 
    scores: scala.collection.immutable.Map[String,Int] = Map(huangbo -> 85, xuzheng -> 99, huanglei -> 90)
    
    scala> val scores = Map(("huangbo",85), ("xuzheng",99), ("huanglei",90)) 
    scores: scala.collection.immutable.Map[String,Int] = Map(huangbo -> 85, xuzheng -> 99, huanglei -> 90)
    

    8.6.2. 获取和修改Map中的值

    scala> val scores = Map("huangbo" -> 85, "xuzheng" -> 99, "huanglei" -> 90) 
    scores: scala.collection.immutable.Map[String,Int] = Map(huangbo -> 85, xuzheng -> 99, huanglei -> 90)
    
    scala> scores("huangbo") 
    res6: Int = 85
    
    scala> scores.getOrElse("huangbo", 100) 
    res7: Int = 85
    
    scala> scores.getOrElse("huangbo11", 100) 
    res8: Int = 100
    

    注意:通常我们在创建一个集合是会用val这个关键字修饰一个变量(相当于java中的final),那么就意 味着该变量的引用不可变,该引用中的内容是不是可变,取决于这个引用指向的集合的类型。

    常见的map操作:

    scala> val studentInfo=Map("john" -> 21, "stephen" -> 22,"lucy" -> 20) 
    studentInfo: scala.collection.immutable.Map[String,Int] = Map(john -> 21, stephen -> 22, lucy -> 20)
    
    scala> studentInfo.clear() 
    <console>:15: error: value clear is not a member of scala.collection.immutable.Map[String,Int]
      8.6.3. Option, None, Some类型 
           studentInfo.clear()
                       ^
    
    scala> val studentInfoMutable=scala.collection.mutable.Map("john" -> 21, "stephen" -> 22,"lucy" -> 20) 
    studentInfoMutable: scala.collection.mutable.Map[String,Int] = Map(john -> 21, lucy -> 20, stephen -> 22)
    
    scala> studentInfoMutable.clear()
    
    scala> studentInfoMutable res11: scala.collection.mutable.Map[String,Int] = Map()
    
    scala> for( i <- studentInfoMutable ) println(i)
    
    scala> val studentInfoMutable=scala.collection.mutable.Map("john" -> 21, "stephen" -> 22,"lucy" -> 20) 
    studentInfoMutable: scala.collection.mutable.Map[String,Int] = Map(john -> 21, lucy -> 20, stephen -> 22)
    
    scala> for( i <- studentInfoMutable ) 
    println(i) (john,21) (lucy,20) (stephen,22)
    
    scala> studentInfoMutable.foreach(e=> println(e._1+":"+e._2)) 
    john:21
    lucy:20 
    stephen:22
    
    scala> val xMap=new scala.collection.mutable.HashMap[String,Int]() 
    xMap: scala.collection.mutable.HashMap[String,Int] = Map()
    
    scala> xMap.put("spark",1) 
    res15: Option[Int] = None
    
    scala> xMap.put("spark",1) 
    res16: Option[Int] = Some(1)
    
    scala> xMap.contains("spark") 
    res17: Boolean = true
    
    scala> val xMap=scala.collection.mutable.Map(("spark",1),("hive",1)) 
    xMap: scala.collection.mutable.Map[String,Int] = Map(spark -> 1, hive -> 1)
    
    scala> "spark" -> 1 
    res18: (String, Int) = (spark,1)
    
    scala> xMap.get("spark") 
    res19: Option[Int] = Some(1)
    
    scala> xMap.get("SparkSQL") 
    res20: Option[Int] = None
    
    

    8.6.3. Option, None, Some类型

    Option、None、Some是scala中定义的类型,它们在scala语言中十分常用,因此这三个类型非学重要。

    None、Some是Option的子类,它主要解决值为null的问题,在java语言中,对于定义好的HashMap,如 果get方法中传入的键不存在,方法会返回null,在编写代码的时候对于null的这种情况通常需要特殊处 理,然而在实际中经常会忘记,因此它很容易引起 NullPointerException异常。

    在Scala语言中通过Option、None、Some这三个类来避免这样的问题,这样做有几个好处,首先是代码可读 性更强,当看到Option时,我们自然而然就知道它的值是可选的,然后变量是Option,比如 Option[String]的时候,直接使用String的话,编译直接通不过。

    几种常见“空”的概念

     Nothing :                 所有类的子类
     None:                     从map中使用get方法获取值,如果没有获取到的就是None
     Nil:                      表示一个空序列
     Null:                     所有引用类的子类
     Unit:                     抽象了void关键字,用来表示一个方法没有返回值得抽象
    

    9. 编程练习

    9.1. 99乘法表

    object Table99Demo {
        
        def main(args: Array[String]): Unit = {
            
            // 传统方式
            for (i <- 1 to 9) {
                for (j <- 1 to i) {
                    printf("%d*%d=%2d\t", i, j, (i * j))
                }
                println()
            }
            
            println("-------------------------------------")
            
            // 新方式1
            for (i <- 1 to 9; j <- 1 to i) {
                printf("%d*%d=%2d\t", i, j, (i * j)); if (i == j) println()
            }
            
            // 新方式2
            for (i <- 1 to 9; j <- 1 to i) {
                print(s"$i*$j=${i * j}${if (i == j) "\n" else "\t"}")
            }
        }
    }
    
    

    9.2. Scala版本的WordCount

    现在有一个数组变量读入了一段话形成了一个数组,求出这段话中的每个单词的出现次数,简单来说, 就是单词统计:

     object WordCountDemo {
        
        def main(args: Array[String]): Unit = {
            
            val array = Array("hello huangbo", "hello xuzheng", "hello wangbaoqiang")
            
            /**
             * 第一种方式
             */
            val result: List[(String, Int)] = array.flatMap(_.split(" "))
              .map((_, 1))
              .groupBy(t => t._1)
              .map(t => (t._1, t._2.length))
              .toList
              .sortBy(t => t._2)
              .reverse
            for (t <- result) {
                println(t)
            }
            println("------------------------------1------------------------------------")
            
            
            /**
             * 第二种方式
             */
            val result2: Map[String, Int] = array.flatMap(_.split(" "))
              .map(x => (x, 1))
              .groupBy(x => x._1)
              .mapValues(x => x.length)
            for ((k, v) <- result2) {
                println(k, v)
            }
            println("------------------------------2------------------------------------")
            
            
            /**
             * 第三种方式
             */
            val result3: Map[String, Int] = array.flatMap(_.split(" "))
              .map(x => (x, 1))
              .groupBy(x => x._1)
              .mapValues(x => x.foldLeft(0)((x, y) => x + y._2))
            for ((k, v) <- result3) {
                println(k, v)
            }
        }
    }
    
    

    结果:

    9.3. Scala版本的插入排序InsertSort

    import scala.util.control.Breaks
    object InsertSortDemo {
        
        def main(args: Array[String]): Unit = {
            
            val array = Array(4, 12, 6, 3, 8, 9, 5)
            val ab = array.toBuffer
            
            // 创建Breaks对象
            val forLoop = new Breaks
            
            for (i <- 1 until ab.length) {
                val value_i = ab(i)
                
                // 把需要可能break的代码放在 breakable 中执行
                forLoop.breakable {
                    for (j <- 0 to i - 1) {
                        
                        val value_j = ab(j)
                        if (value_j > value_i) {
                            ab.remove(i, 1)
                            ab.insert(j, value_i)
                            
                            // 使用break进行跳出
                            forLoop.break()
                        }
                    }
                }
            }
            println(ab)
        }
        
    }
    
    

    另一种实现方式:

    def isort(xs: List[Int]): List[Int] = if (xs.isEmpty) Nil else insert(xs.head, isort(xs.tail))
    
    def insert(x: Int, xs: List[Int]): List[Int] = if (xs.isEmpty || x <= xs.head) x :: xs else xs.head :: insert(x, xs.tail)
    

    相关文章

      网友评论

        本文标题:Scala基础语法

        本文链接:https://www.haomeiwen.com/subject/isfpmktx.html