Scala 04 —— Scala Puzzle 拓展

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: Scala 程序设计探讨了占位符、模式匹配、继承中的成员声明、默认值与重载以及集合操作的一致性。示例展示了 `_` 占位符在函数简洁性上的应用,同时指出它不等同于箭头函数,因为函数体内的副作用可能不同。另外,解释了变量与常量模式,以及在继承中字段初始化的顺序。在集合操作中,`for`循环与`map`的区别在于`for`会过滤不符合模式的元素,而`map`则会引发错误。最后,讨论了如何确保集合类型在操作中保持一致。

image.png
image.png

Scala 04 —— Scala Puzzle 拓展

@[TOC]

一、占位符

val list1:List[Int] = List(1,2,3).map(_ + 1)
val list2:List[Int] = List(1,2,3).map(r => r + 1)

在上面的例子中,两代码的返回结果其实是一样的,都是 List(2,3,4),而在第一行中的 _叫做占位符,可以让我们的代码更加简洁 但是这并不意味着使用 _ 和箭头函数是同样的结果

val list1:List[Int] = List(1,2,3).map{println("ss");_ + 1}
val list2:List[Int] = List(1,2,3).map{r => println("ss");r + 1}

在上面的代码中,看起来运行的结果还是一样的,但是事实却不是这样的,当然 list1list2的返回值是一样的,但是执行的过程中打印的结果却是不一样的, 第一行代码打印了一行 ss,第二行代码打印了三行 ss

为什么会有这样的不同?

Map函数的本质是对每个集合中的元素应用一个函数,第一个语句中函数的执行体只是_+1,第二个语句中函数的执行体是println("ss");r + 1

二、模式匹配的变量和常量模式

  • 变量模式:模式匹配的标识符是小写字母,作用是赋值

    val (x,y) = (1,2)
    
  • 常量模式:模式匹配的标识符是大写字母,该常量必须是已赋值的,否则会报错,作用是判断

    val (X,Y) = (1,2)// 报错
    x match {
      case PI => "圆周率"
    }
    

三、继承 成员声明的位置

trait A{
  val audience:String
  println("Hello " + audience)
}
class BMember (a:String = "World") extends A{
  override val audience: String = a
  println("I repeat:Hello " + audience)
}

class BConstructor(val audience:String = "World") extends A{
  //该种方法声明的变量不会存在null的情况
  println("I repeat:Hello " + audience)
}
new BMember("reader")
new BConstructor("reader")
结果
Hello null
I repeat:Hello reader
Hello reader
I repeat:Hello reader
初始化顺序分析
BMember
  1. 调用构造器:当创建 BMember 的实例时,首先初始化父类 A
  2. 父类 A 的初始化:在 Scala 中,父类的构造代码块(包括字段的初始化和任何其他语句)首先被执行。在 A 中,audience 还未被 BMember 初始化,因此其值为 nullString 类型的默认值)。
  3. 打印语句执行:打印 "Hello " + audience,由于 audience 还是 null,输出 Hello null
  4. 子类 BMember 的字段初始化:初始化 audience 为传入的参数 "reader"
  5. 子类中的打印语句:接着执行 BMember 中的打印语句,输出 "I repeat: Hello reader"
BConstructor
  1. 构造器参数:在 BConstructor 类中,audience 是通过构造器参数直接定义的。这意味着在调用父类 A 的构造器之前,audience 已经被初始化。
  2. 父类 A 的初始化:由于 audience 已经初始化为 "reader",当父类 A 中的打印语句执行时,输出 "Hello reader"
  3. 子类中的打印语句:紧接着在 BConstructor 中,再次打印 "I repeat: Hello reader"

总结:

一般来说,子类在初始化时会先初始化父类构造器构造出父类对象,因为子类可能有依赖于父类对象的属性或方法。

作为类字段被赋值,在父类构造器执行后才初始化;作为构造参数被赋值,在父类构造器执行前初始化。

四、缺省初始值与重载

trait A {
  val foo: Int //缺省初始值,Boolean缺省初始值是false,Unit缺省初始值是()
  def bar: Int = 10 //附初值的变量后面只能用override
  println(s"In A:foo=$foo,bar=$bar") //0,0,0
}

class B extends A {
  val foo: Int = 25
  println(s"In B:foo=$foo,bar=$bar") //25,36,0

}
class C extends B {
  override val foo: Int = 46 //当一个val被重载的时候,只能初始化一次

  override def bar: Int = 100

  println(s"In C:foo=$foo,bar=$bar") //25,36,99

}

new C()

/*
In A:foo=0,bar=100
In B:foo=0,bar=100
In C:foo=46,bar=100
*/
  • 字段初始化顺序:像val foo:Int,字段初始化发生在构造器体执行之前,但超类的构造器(包括 println 语句)会在任何子类字段初始化之前执行。
  • 方法动态绑定:像def bar:Int=5,Scala 会使用动态绑定来决定应该调用哪个版本的方法。即使在超类的构造器中,也会调用最终重写的方法版本(在 C 中为 100)。

  • 字段重写foo 在子类中被重写,但在超类和任何父类的构造器中引用这个字段时,它们看到的是其默认值(在赋值之前),直到子类自己的构造器赋予它新值。

  • 当一个 val被重载,只能初始化一次

五、Scala的集合操作和集合类型保持一致性

def sumSizes(collections:Iterable[Iterable[_]]):Int = {
  //      println(s"collections:$collections")
  //      println(collections.map(_.size))
  collections.map(_.size).sum
}
第一部分代码解释
println(sumSizes(List(Set(1,2),List(3,4))))

在这里,输入是List类型的,包含两个集合:一个Set和一个ListList映射(map)操作会返回一个新的List,其中包含每个子集合的大小:

  • Set(1, 2)的大小为2(因为集合中不允许重复值)
  • List(3, 4)的大小为2

因此,map(_.size)返回List(2, 2),其和为4。

第二部分代码解释
println(sumSizes(Set(List(1,2),Set(3,4))))

这里输入是Set类型的。由于Setmap操作后仍保持Set类型,而且不允许重复值,它会影响结果:

  • List(1, 2)的大小为2
  • Set(3, 4)的大小也是2

由于结果Set不能有重复值,map(_.size)产生的结果是Set(2),其和为2,因为只有一个元素。

def sumSizes1(collections:Iterable[Iterable[_]]):Int = {
  collections.toSeq.map(_.size).sum
}

不管是什么集合类型,都将其转换成Seq

六、for和map的区别

val ys = for(Seq(x,y,z) <- xs) yield x+y+z

这里的for表达式等价于先进行withFilter过滤掉不符合模式Seq(x, y, z)的元素,然后对过滤后的元素应用map。因此,Seq("g","h")因为只有两个元素而被忽略,不参与后续的map操作。

val zs1 = xs map {case Seq(x,y,z) =>x+y+z}

当遇到Seq("g", "h")时,模式Seq(x, y, z)无法匹配只有两个元素的序列,因此Scala抛出了MatchError

image.png

目录
相关文章
|
6月前
|
并行计算 前端开发 安全
Scala
Scala
52 0
|
6月前
|
分布式计算 Java 大数据
Scala01 —— Scala基础
Scala 是一种基于 JVM 的多范式编程语言,它融合了面向对象和函数式编程的特点。本文档介绍了Scala的基础知识,包括如何搭建开发环境、语言特性、变量和数据类型、程序逻辑、运算符、控制流、集合以及方法和函数的使用。特别强调了Scala集合的重要性,它们作为数据结构支持多种操作,并且有许多内置的算子和库来解决问题。文档还涵盖了如何在IDEA中配置Scala SDK,以及如何使用元组、映射、数组和模式匹配等核心概念。此外,文档还讨论了Scala与Java变量的区别,以及如何通过隐式类和字符串插值等扩展语言功能。
53 6
Scala01 —— Scala基础
|
Scala 索引 容器
四天掌握Scala(4)
你好看官,里面请!今天笔者讲的是scala。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。
129 1
四天掌握Scala(4)
|
Java 编译器 Scala
四天掌握Scala(2)
你好看官,里面请!今天笔者讲的是scala。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。
140 1
四天掌握Scala(2)
|
SQL 分布式计算 算法
四天掌握Scala(3)
你好看官,里面请!今天笔者讲的是scala。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。
177 0
四天掌握Scala(3)
|
SQL Java 大数据
四天掌握Scala(1)
你好看官,里面请!今天笔者讲的是scala。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。
194 0
四天掌握Scala(1)
|
Java Scala
Scala学习一
Scala中的方法与函数: 方法是属于类或者对象的,在运行时,它是加载到JVM的方法区中,而函数则是加载到JVM的堆内存中,同时可以将函数对象赋值给一个变量。函数是一个对象,继承自FunctionN,函数对象有apply、curried、toSting、tupled这些方法,而方法则没有。这是scala与java不同的地方。
97 0
Scala学习一
|
SQL 分布式计算 Scala
Scala学习总结
Scala学习总结
130 0
|
分布式计算 Scala Spark
scala中的self =>
scala中的self =>
186 0
|
分布式计算 JavaScript 前端开发
Scala 必知必会
Scala 必知必会 3
118 0
Scala 必知必会