大数据处理学习笔记五:掌握Scala内建控制结构

本文详细介绍了Scala编程中的条件表达式,包括语法格式、执行情况和案例演示,展示了如何根据输入值进行判断并改写程序为函数式风格。此外,还讲解了块表达式的执行规则和案例,以及for循环的单重和多重形式,包括遍历字符串、计算序列和打印九九表等多种应用场景。文章还涉及了异常处理和变量作用域,帮助读者深入理解Scala的控制结构和编程范式。
摘要由CSDN通过智能技术生成

一、条件表达式
(一)语法格式
if (条件) 值1 else 值2
1
(二)执行情况
条件为真,结果是值1;条件为假,结果是值2。如果if和else的返回结果同为某种类型,那么条件表达式结果也是那种类型,否则就是Any类型
Scala的条件表达式有点类似于Java的条件运算符
(三)案例演示
任务1、根据输入值的不同进行判断


当然也可以在一个表达式中进行多次判断


可以将上述条件表达式改造成嵌套的选择结构,可读性倒是提高了,但是简洁性降低了


课堂练习:利用选择表达式评定学生成绩等级


函数式编程语言,只有一个入口和一个出口,中间没有任何与外界交流的输入或输出语句,所以安全性非常好。

Java不是函数式语言,选择结构没有返回值,就必须根据不同情况对评语变量赋值。


任务2、编写Scala程序,判断奇偶性
打开Scala项目ScalaDemo,创建net.huawei.day02包,在包里创建Example01对象
package net.huawei.day02

import scala.io.StdIn

/**
 * 功能:判断奇偶性
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example01 {
  def main(args: Array[String]): Unit = {
    print("n = ")
    val n = StdIn.readLine.toInt
    if (n % 2 == 0)
      println(n.toString + "是偶数~")
    else
      println(n.toString + "是奇数~")
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
运行程序,查看结果

利用if结构具有返回值的特性,改写程序成为函数式风格

package net.huawei.day02

import scala.io.StdIn

/**
 * 功能:判断奇偶性
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example01 {
  def main(args: Array[String]): Unit = {
    print("n = ")
    val n = StdIn.readLine.toInt
    val result = if (n % 2 == 0) n.toString + "是偶数~" else n.toString + "是奇数~"
    println(result)
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
运行程序,查看结果

二、块表达式
(一)语法格式
{语句组}
1
块表达式为包含在符号“{}”中的语句块
(二)执行情况
需要注意的是,Scala中的返回值是最后一条语句的执行结果,而不需要像Java一样单独写return关键字。如果表达式中没有执行结果,就返回一个Unit对象,类似Java中的void。
(三)案例演示
语句块最后一句的值就是整个块表达式的结果


语句块最后一句没有执行结果,那么块表达式结果就是Unit


三、for循环
(一)单重for循环
1、语法格式
for (变量 <- 集合或数组 (条件)) {
     语句组
}
1
2
3
2、执行情况
表示将集合或数组中的每一个值循环赋给一个变量
3、案例演示
任务1:输出1到10
两种方式实现,一种使用Range类,一种使用to运算符


Range(a, b): 从a到b,不包含b,跟Python里的range函数一样,含头不含尾

1 to 10表示将1到10的所有值组成一个集合,且包括10。若不想包括10,则可使用关键字until


用Java语言完成任务


用Python语言完成任务


打印字符直角三角形


利用map函数产生每行星号构成的向量,然后利用foreach函数循环输出


利用双重循环与流间变量,这个是Scala语言特有的东西


任务2、遍历字符串,输出每个字符
方法一、按索引取字符串的每个字符(传统for循环)


方法二:将字符串看作一个由多个字符组成的集合(增强for循环)


任务3、计算 1 + 2 + 3 + … + 100
注意sum必须定义为var型变量

利用集合的归并方法来求和最简单

大家有兴趣可以去了解scala集合的聚合函数
任务4、输出列表内的偶数(过滤)
采用三种方法来实现


采用Java语言实现


采用Python语言实现


任务5、输出全部两位素数
for (n <- 10 to 100; if !(n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0))  print(n.toString + " ")
1


(10 to 100).filter(n => !(n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0))
1


课堂练习:输出100~500之间的全部素数,并统计素数个数
(二)嵌套for循环
1、语法格式
传统格式
for (变量1 <- 集合或数组(条件))
    for (变量2 <- 集合或数组(条件)) {
       语句组
    }
}
1
2
3
4
5
特有格式
for (变量1 <- 集合或数组; 变量2 <- 集合或数组 (条件)) {
   语句组
}
1
2
3
2、案例演示
任务1、打印九九表
方法一、采用双重循环来实现
在net.huawei.day02包里创建Example02对象

package net.huawei.day02

/**
 * 功能:双重循环打印九九表
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example02 {
  def main(args: Array[String]): Unit = {
    for (i <- 1 to 9) {
      for (j <- 1 to i) {
        print(i.toString + "×" + j + "=" + (i * j) + "\t")
      }
      println()
    }
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
运行程序,查看结果

方法二、采用单重循环来实现
在net.huawei.day02包里创建Example03对象

package net.huawei.day02

/**
 * 功能:单重循环打印九九表
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example03 {
  def main(args: Array[String]): Unit = {
    for (i <- 1 to 9; j <- 1 to i) {
      print(i.toString + "×" + j + "=" + (i * j) + "\t")
      if (j == i) println()
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
运行程序,查看结果

方法三、采用单重循环与流间变量绑定来实现
在net.huawei.day02包里创建Example04对象
在for循环头里sep = if (i == j) "\n" else "\t"就是流间变量绑定

package net.huawei.day02

/**
 * 功能:单重循环与流间变量绑定打印九九表
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example04 {
  def main(args: Array[String]): Unit = {
    for (i <- 1 to 9; j <- 1 to i; sep = if (j == i) "\n" else "\t") {
      print(i.toString + "×" + j + "=" + (i * j) + sep)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
运行程序,查看结果

方法四、采用单重循环、流间变量与yield来实现
for循环语句本身的返回值是Unit类型,无论在循环体中返回什么都是无效的,最终得到的都是Unit的值,但是可以在循环中的循环条件和循环体之间加上yield关键字,那么就可以将循环体产生的返回值组成数组进行返回。
在net.huawei.day02包里创建Example05对象

package net.huawei.day02

/**
 * 功能:采用单重循环、流间变量与yield打印九九表
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example05 {
  def main(args: Array[String]): Unit = {
    val list = for (i <- 1 to 9; j <- 1 to i; sep = if (j == i) "\n" else "\t")
      yield i.toString + "×" + j + "=" + (i * j) + sep
    list.foreach(print)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
运行程序,查看结果

任务2、去掉对角线
一个三阶方阵,单元格的值是行号与列号的乘积,去掉对角线,输出剩余元素

方法一、传统双重循环

package net.huawei.day02

/**
 * 功能:去掉对角元素
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example06 {
  def main(args: Array[String]): Unit = {
    for (i <-1 to 3) {
      for (j <- 1 to 3) {
        if (i != j)
          print((i * j).toString + "\t")
        else
          print("\t")
      }
      println()
    }
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
运行程序,查看结果

方法二、特有双重循环

package net.huawei.day02

/**
 * 功能:去掉对角线元素
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example07 {
  def main(args: Array[String]): Unit = {
    for (i <- 1 to 3; j <- 1 to 3; sep = if (i == j) "\t" else (i * j).toString + "\t") {
      print(sep)
      if (j == 3) println()
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
运行程序,查看结果

方法三:采用两个流间变量和yield

package net.huawei.day02

/**
 * 功能:去掉对角线元素
 * 作者:华卫
 * 日期:2023年02月22日
 */
object Example08 {
  def main(args: Array[String]): Unit = {
    val list = for (i <- 1 to 3; j <- 1 to 3; sep = if (j == 3) "\n" else "\t";
                    str = if (i != j) (i * j).toString + sep else " " + sep) yield str
    list.foreach(print)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
运行程序,查看结果

课堂练习:编程求解百钱买百鸡问题
我国古代数学家张丘建在《算经》一书中曾提出过著名的“百钱买百鸡”问题,该问题叙述如下:鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,则翁、母、雏各几何?
翻译过来,意思是公鸡一个五块钱,母鸡一个三块钱,小鸡三个一块钱,现在要用一百块钱买一百只鸡,问公鸡、母鸡、小鸡各多少只?
{ c o c k + h e n + c h i c k = 100 ( 1 ) c o c k × 5 + h e n × 3 + c h i c k 3 = 100 ( 2 )
⎧⎩⎨cock+hen+chick=100cock×5+hen×3+chick3=100(1)(2)
{




+



+





=
100
(
1
)




×
5
+



×
3
+





3
=
100
(
2
)




  
cock+hen+chick=100
cock×5+hen×3+ 
3
chick

 =100

  
(1)
(2)

 

大家可以利用Scala特有的双重循环和流间变量来处理
四、条件循环
(一)while循环
1、语法格式
Scala的while循环与Java类似
while(条件) {   
   循环体
}
1
2
3
2、案例演示
任务1、计算1+ 2 + 3 + … + 100
在net.huawei.day02包里创建Example10对象


注意:i、sum必须是变量

package net.huawei.day02

/**
 * 功能:等差数列求和
 * 作者:华卫
 * 日期:2023年02月23日
 */
object Example10 {
  def main(args: Array[String]): Unit = {
    var sum = 0
    var i = 1
    while (i <= 100) {
      sum = sum + i
      i = i + 1
    }
    println("1 + 2 + 3 + ... + 100 = " + sum)
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行程序,查看结果

采用函数式风格来写代码,递归函数来实现求和,注意:此处sum是常量
在net.huawei.day02包里创建Example10_对象

package net.huawei.day02

/**
 * 功能:等差数列求和
 * 作者:华卫
 * 日期:2023年02月23日
 */
object Example10_ {
  def mx(n: Int, sum: Int): Int = {
    if (n > 0) mx(n - 1, sum + n) else sum
  }

  def main(args: Array[String]): Unit = {
    println("1 + 2 + 3 + ... + 100 = " + mx(100, 0))
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
运行程序,查询结果

任务2、打印全部水仙花数
所谓水仙花数,是指等于其各位数字立方和的三位数。
\qquad153 = 1 3 + 5 3 + 3 3 153=1^3+5^3+3^3153=1 
3
 +5 
3
 +3 
3
 
\qquad370 = 3 3 + 7 3 + 0 3 370=3^3+7^3+0^3370=3 
3
 +7 
3
 +0 
3
 
\qquad371 = 3 3 + 7 3 + 1 3 371=3^3+7^3+1^3371=3 
3
 +7 
3
 +1 
3
 
\qquad407 = 4 3 + 0 3 + 7 3 407=4^3+0^3+7^3407=4 
3
 +0 
3
 +7 
3
 

对于[100, 999]范围的每个数n,我们要去判断它是否等于其各位数字的立方和,这里的难点或关键在于如何分解一个三位数,得到它的每位数字。

假设我们已经把三位数n分解成百位数p3,十位数p2,个位数p1,
这样我们的筛选条件就可以写出来:n == p3 * p3 * p3 + p2 * p2 * p2 + p1 * p1 * p1。

如何拆分一个三位数n呢?

首先求n的个位数:n % 10
然后要将三位数变成两位数:n = n / 10;
对于新的两位数n,又求它的个位数:n % 10
然后要将两位数变成一位数:n = n / 10;

也就是说我们可以交替使用求余和整除运算将一个三位数拆分,得到它的个位数、十位数和百位数。当然这个分解方法可以推广到任何多位数的拆分。

在net.huawei.day02包里创建Example11对象


package net.huawei.day02

/**
 * 功能:打印水仙花数
 * 作者:华卫
 * 日期:2023年02月23日
 */
object Example11 {
  def main(args: Array[String]): Unit = {
    for (n <- 100 to 999) {
      val p1 = n % 10     // 个位数
      val p2 = n / 10 % 10 // 十位数
      val p3 = n / 100     // 百位数
      if (n == p1 * p1 * p1 + p2 * p2 * p2 + p3 * p3 * p3)
        println(n.toString + " = " + p3 + "^3 + " + p2 + "^3 + " + p1 + "^3")
    }
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行程序,查看结果

课堂练习:利用字符串来截取来处理,打印水仙花数

字符转换成整数其实是ASCII码,'0'的ASCII码是48,因此要减48才可以。

Java里字符串截取字符再转换成整数

Python里字符串截取再转换成整数

Scala里字符串截取再转换成整数

课堂练习:字符串拆分(整数是可以转换成字符串来处理的)

(二)do while循环
与Java语言一样,do while循环与while循环类似,但是do while循环会确保至少执行一次循环。
1、语法格式
do {
   循环体
} while(条件)
1
2
3
2、案例演示
任务:计算1+ 2 + 3 + … + 100
在net.huawei.day02包里创建Example12对象

package net.huawei.day02

/**
 * 功能:等差数列求和
 * 作者:华卫
 * 日期:2023年02月23日
 */
object Example12 {
  def main(args: Array[String]): Unit = {
    var sum = 0
    var i = 1
    do {
      sum = sum + i
      i = i + 1
    } while (i <= 100)
    println("1 + 2 + 3 + ... + 100 = " + sum)
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行程序,查看结果

五、异常处理
(一)异常处理概述
Scala中继承了Java的异常机制,提供了程序中产生意外情况时处理的机制,抛出异常的过程和Java中基本一致,通过throw关键字进行:throw XxxException(),一旦抛出可以当场捕获处理或接着向上抛,捕获异常是通过 try-catch-finally来实现的。
(二)案例演示
任务:演示try-catch-finally
try-catch-finally是有返回值的:如果没有异常就是try语句的返回值,如果有异常就是catch语句的返回值。注意不会是finally的返回值,finally即使有返回值,也会被抛弃,这点和Java是不同的。
在net.huawei.day02包里创建Example13对象

package net.huawei.day02

import java.io.IOException

/**
 * 功能:演示异常处理
 * 作者:华卫
 * 日期:2023年03月01日
 */
object Example13 {
  def main(args: Array[String]): Unit = {
    var message = ""
    val result = try {
      mx() // 调用方法,会抛出异常
      "恭喜,程序执行正常~"
    } catch {
      case e: NullPointerException => "空指针异常~"
      case e: IOException => "呵呵,这是I/O异常~"
      case e: RuntimeException => "哈哈,这是运行时异常~"
      case e: Exception => "管它呢,反正是异常~"
    } finally {
      message = "程序到此为止~"
      "无论是否有异常,都会执行finally里的语句~"
    }    
    println(result)
    println(message)
  }

  def mx(): Unit = {
    throw new RuntimeException("故意抛出一个运行时异常~")
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
执行程序,查看结果(此时有异常,result取的是catch里的返回值 - 哈哈,这是运行时异常~,finally语句块执行了的,因此message可以打印出来 - 程序到此为止!)


注释掉mx()方法里的语句


执行程序,查看结果(此时有异常,result取的是try里的返回值 - 恭喜,程序执行正常!)


在Scala里,finally的返回值不会覆盖try和catch的返回值,这一点跟Java不同。


运行程序,查看结果(此时运行test()方法,调用mx()方法抛出异常,执行catch语句块,返回异常~,但是要被finally语句块的返回值停止!覆盖,因此最终输出的就是停止!)


我们把mx()方法里的抛出异常的语句注释掉,此时程序正常运行,但是test()方法的返回值不会是恭喜,程序运行正常~,还是会被finally语句块的返回值覆盖,成为无论如何,程序结束~


六、match结构
(一)语法格式
变量 match {
      case 值1 => 表达式1或语句1
      case 值2 => 表达式2或语句2
      case 值3 => 表达式3或语句3
      ……
      case _ => 表达式n或语句n
    }
1
2
3
4
5
6
7
Scala中的match(匹配)类似于其他语言的switch(开关)。与Java不同的是,match语句可以应用在任何类型量或表达式上,另外不需要调用break语句,match默认执行完一个case后直接跳出match结构。注意,match是具有返回值的,就是被选到的case的值。
(二)案例演示
任务:给城市下评语
在net.huawei.day02包里创建Example14对象

package net.huawei.day02

import scala.io.StdIn

/**
 * 功能:给城市下评语
 * 作者:华卫
 * 日期:2023年03月01日
 */
object Example14 {
  def main(args: Array[String]): Unit = {
    print("输入城市:")
    val city = StdIn.readLine()
    val comment = city match {
      case "北京" => "是伟大的首都~"
      case "上海" => "是神奇的魔都~"
      case "广州" => "是迷人的妖都~"
      case "泸州" => "是醉人的酒城~"
      case _ => "是普通的城市~"
    }
    println(city + comment)
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
运行程序,查看结果

课堂练习:采用if结构来完成城市评语任务
七、变量作用域
(一)Java变量作用域
Java中根据不同大括号区分变量作用范围,不允许有叠加,外部看不到内部,内部能看到外部。
1、内部能访问外部
代码块里能访问代码块之前定义的变量

代码块里不能访问代码块之后定义的变量

2、外部不能看内部
代码块里定义的变量一旦越出代码块就失效

(二)Scala变量作用域
Scala中根据不同大括号区分变量作用范围,不允许有叠加,外部看不到内部,内部看不到外部。
1、内部不能访问外部
在net.huawei.day02包创建Example15对象

package net.huawei.day02

/**
 * 功能:变量作用域 - 内部能访问外部
 * 作者:华卫
 * 日期:2023年03月01日
 */
object Example15 {
  def main(args: Array[String]): Unit = {
    val message = "欢迎访问醉美泸州~"
    if (true) {
      println(message)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
运行程序,查看结果

2、外部不能访问内部
在net.huawei.day02包创建Example16对象

这个情形有点类似于豪车,外面的人不能透过车窗看见里面在干嘛,但是车里的人却可以清楚地看见外面的人在干嘛。
八、补充案例
任务:评定成绩等级
1、编写符合函数式编程风格的Sala程序
在net.huawei.day02包里创建Example17对象

package net.huawei.day02

import scala.io.StdIn

/**
 * 功能:成绩等级评定(采用函数式风格)
 * 作者:华卫
 * 日期:2023年03月01日
 */
object Example17 {
  def main(args: Array[String]): Unit = {
    print("score = ")
    val score = StdIn.readLine().toInt

    val comment = if (score > 100) {
      "超出范围"
    } else if (score >= 90) {
      "优秀"
    } else if (score >= 80) {
      "良好"
    } else if (score >= 70) {
      "中等"
    } else if (score >= 60) {
      "及格"
    } else if (score >= 0) {
      "不及格"
    } else {
      "超出范围"
    }

    println("评语:" + comment)
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
运行程序,查看结果

2、函数式风格的程序有几个特点
if结构像函数一样有返回值
if结构里除了传入参数score之外,没有别的变量。
-if结构里没有与外界交流,比如输入或输出或网络连接或读取文件之类。
函数式编程是为了处理计算,不考虑系统的读写(I/O)。
函数式编程强调没有副作用(指的是函数内部与外部互动,产生运算以外的其它结果)。函数要求独立,只返回一个值,没有其它行为,尤其不能修改外部变量的值。
3、非函数式风格的程序
看看下面这个程序 - Example18

package net.huawei.day02

import scala.io.StdIn

/**
 * 功能:成绩等级评定(采用非函数式风格)
 * 作者:华卫
 * 日期:2023年03月01日
 */
object Example18 {
  def main(args: Array[String]): Unit = {
    print("score = ")
    val score = StdIn.readLine().toInt

    var comment = ""
    if (score > 100) {
      comment = "超出范围"
    } else if (score >= 90) {
      comment = "优秀"
    } else if (score >= 80) {
      comment = "良好"
    } else if (score >= 70) {
      comment = "中等"
    } else if (score >= 60) {
      comment = "及格"
    } else if (score >= 0) {
      comment = "不及格"
    } else {
      comment = "超出范围"
    }

    println("评语:" + comment)
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
虽然处理结果跟上面那个程序一样,但是并不符合函数式编程风格。因为if结构有副作用,修改了if结构之外的变量comment的值。

再看看下面这个程序 - Example19


package net.huawei.day02

import scala.io.StdIn

/**
 * 功能:成绩等级评定(采用非函数式风格)
 * 作者:华卫
 * 日期:2023年03月01日
 */
object Example19 {
  def main(args: Array[String]): Unit = {
    print("score = ")
    val score = StdIn.readLine().toInt

    if (score > 100) {
      println("评语:超出范围")
    } else if (score >= 90) {
      println("评语:优秀")
    } else if (score >= 80) {
      println("评语:良好")
    } else if (score >= 70) {
      println("评语:中等")
    } else if (score >= 60) {
      println("评语:及格")
    } else if (score >= 0) {
      println("评语:不及格")
    } else {
      println("评语:超出范围")
    }
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
不符合函数式编程风格。因为if结构有副作用,有写操作。
————————————————
版权声明:本文为CSDN博主「howard2005」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/howard2005/article/details/129121298

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值