【大数据】初识Scala 2

简介: 【大数据】初识Scala

传名参数

传名参数(Call-by-Name Parameters)是一种特殊的参数传递方式,它允许我们将表达式作为参数传递给函数,并在需要时进行求值。传名参数使用 => 符号来定义,以表示传递的是一个表达式而不是具体的值。下面是关于传名参数的解释和示例代码:

传名参数的特点是,在每次使用参数时都会重新求值表达式,而不是在调用函数时进行求值。这样可以延迟表达式的求值,只在需要时才进行计算。传名参数通常用于需要延迟计算、惰性求值或者需要按需执行的场景。

下面是一个示例代码:

def callByName(param: => Int): Unit = {
  println("Inside callByName")
  println("Param 1: " + param)
  println("Param 2: " + param)
}
def randomNumber(): Int = {
  println("Generating random number")
  scala.util.Random.nextInt(100)
}
callByName(randomNumber())

在上述示例中,定义了一个名为 callByName 的函数,它接受一个传名参数 param。在函数体内,我们打印出两次参数的值。

另外,定义了一个名为 randomNumber 的函数,它用于生成随机数。在该函数内部,我们打印出生成随机数的消息,并使用 scala.util.Random.nextInt 方法生成一个介于 0 到 100 之间的随机数。

在主程序中,我们调用 callByName 函数,并将 randomNumber() 作为传名参数传递进去。

当程序执行时,会先打印出 "Inside callByName" 的消息,然后两次调用 param,即 randomNumber()。在每次调用时,都会重新生成一个新的随机数,并打印出相应的值。

这说明传名参数在每次使用时都会重新求值表达式,而不是在调用函数时进行求值。这样可以实现按需执行和延迟计算的效果。

implicit

implicit 关键字用于定义隐式转换和隐式参数。它可以用来简化代码,让编译器自动执行一些操作。

下面是一些使用 implicit 关键字的示例:

  • 隐式转换:可以使用 implicit 关键字定义隐式转换函数,让编译器自动将一种类型的值转换为另一种类型的值。
implicit def intToString(x: Int): String = x.toString
val x: String = 1
println(x) // 输出 "1"

在这个例子中,定义了一个隐式转换函数 intToString,它接受一个 Int 类型的参数,并返回它的字符串表示。由于这个函数被定义为 implicit,因此编译器会在需要时自动调用它。

在主程序中,我们将一个 Int 类型的值赋值给一个 String 类型的变量。由于类型不匹配,编译器会尝试寻找一个隐式转换函数来将 Int 类型的值转换为 String 类型的值。在这个例子中,编译器找到了我们定义的 intToString 函数,并自动调用它将 1 转换为 "1"

  • 隐式参数:可以使用 implicit 关键字定义隐式参数,让编译器自动为方法提供参数值。
implicit val x: Int = 1
def foo(implicit x: Int): Unit = println(x)
foo // 输出 1

在这个例子中,定义了一个隐式值 x 并赋值为 1。然后我们定义了一个方法 foo,它接受一个隐式参数 x

在主程序中,我们调用了方法 foo,但没有显式地传入参数。由于方法 foo 接受一个隐式参数,因此编译器会尝试寻找一个隐式值来作为参数传入。在这个例子中,编译器找到了我们定义的隐式值 x 并将其作为参数传入方法 foo

Object和Class

在Scala中,classobject 都可以用来定义类型,但它们之间有一些重要的区别。class 定义了一个类,它可以被实例化。每次使用 new 关键字创建一个类的实例时,都会创建一个新的对象。

class MyClass(x: Int) {
  def printX(): Unit = println(x)
}
val a = new MyClass(1)
val b = new MyClass(2)
a.printX() // 输出 1
b.printX() // 输出 2

构造器可以通过提供一个默认值来拥有可选参数:

class Point(var x: Int = 0, var y: Int = 0)
val origin = new Point  // x and y are both set to 0
val point1 = new Point(1)
println(point1.x)  // prints 1

在这个版本的Point类中,xy拥有默认值0所以没有必传参数。然而,因为构造器是从左往右读取参数,所以如果仅仅要传个y的值,你需要带名传参。

class Point(var x: Int = 0, var y: Int = 0)
val point2 = new Point(y=2)
println(point2.y)  // prints 2

object 定义了一个单例对象。它不能被实例化,也不需要使用 new 关键字创建。在程序中,一个 object 只有一个实例。此外,object 中定义的成员都是静态的,这意味着它们可以在不创建实例的情况下直接访问。而 class 中定义的成员只能在创建实例后访问。

object MyObject {
  val x = 1
  def printX(): Unit = println(x)
}
MyObject.printX() // 输出 1

另外,在Scala中,如果一个 object 的名称与一个 class 的名称相同,那么这个 object 被称为这个 class 的伴生对象。伴生对象和类可以相互访问彼此的私有成员:

class MyClass(x: Int) {
  private val secret = 42
  def printCompanionSecret(): Unit = println(MyClass.companionSecret)
}
object MyClass {
  private val companionSecret = 24
  def printSecret(c: MyClass): Unit = println(c.secret)
}
val a = new MyClass(1)
a.printCompanionSecret() // 输出 24
MyClass.printSecret(a) // 输出 42

在这个例子中,定义了一个类 MyClass 和它的伴生对象 MyClass。类 MyClass 中定义了一个私有成员变量 secret 和一个方法 printCompanionSecret,用于打印伴生对象中的私有成员变量 companionSecret。而伴生对象 MyClass 中定义了一个私有成员变量 companionSecret 和一个方法 printSecret,用于打印类 MyClass 的实例中的私有成员变量 secret

在主程序中,创建了一个类 MyClass 的实例 a,并调用了它的 printCompanionSecret 方法。然后我们调用了伴生对象 MyClassprintSecret 方法,并将实例 a 作为参数传入。

这就是Scala中类和伴生对象之间互相访问私有成员的基本用法。

样例类

样例类(case class)是一种特殊的类,常用于描述不可变的值对象(Value Object)。它们非常适合用于不可变的数据。定义一个样例类非常简单,只需在类定义前加上case关键字即可。例如,下面是一个简单的样例类定义:

case class Person(var name: String, var age: Int)

创建样例类的实例时,不需要使用new关键字,直接使用类名即可。例如,下面是一个创建样例类实例并修改其成员变量的示例:

object Test01 {
  case class Person(var name: String, var age: Int)
  def main(args: Array[String]): Unit = {
    val z = Person("张三", 20)
    z.age = 23
    println(s"z = $z")
  }
}

_(下划线)

在Scala中,下划线 _ 是一个特殊的符号,它可以用在许多不同的地方,具有不同的含义。

  • 作为通配符:下划线可以用作通配符,表示匹配任意值。例如,在模式匹配中,可以使用下划线来表示匹配任意值。
x match {
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}
  • 作为忽略符:下划线也可以用来忽略不需要的值。例如,在解构赋值时,可以使用下划线来忽略不需要的值。
val (x, _, z) = (1, 2, 3)
  • 作为函数参数占位符:下划线还可以用作函数参数的占位符,表示一个匿名函数的参数。例如,在调用高阶函数时,可以使用下划线来简化匿名函数的定义。
val list = List(1, 2, 3)
list.map(_ * 2)
  • 将方法转换为函数:在方法名称后加一个下划线,会将其转化为偏应用函数(partially applied function),就能直接赋值了。
def add(x: Int, y: Int) = x + y
val f = add _

这只是下划线在Scala中的一些常见用法。由于下划线在不同的上下文中具有不同的含义,因此在使用时需要根据具体情况进行判断。

println

println 函数用于向标准输出打印一行文本。它可以接受多种不同类型的参数,并将它们转换为字符串进行输出。

下面是一些常见的使用 println 函数进行输出的方式:

  • 输出字符串:直接将字符串作为参数传入 println 函数,它会将字符串原样输出。
println("Hello, world!")
  • 输出变量:将变量作为参数传入 println 函数,它会将变量的值转换为字符串并输出。
val x = 1
println(x)
  • 输出表达式:将表达式作为参数传入 println 函数,它会计算表达式的值并将其转换为字符串输出。
val x = 1
val y = 2
println(x + y)
  • 使用字符串插值:可以使用字符串插值来格式化输出。在字符串前加上 s 前缀,然后在字符串中使用 ${expression} 的形式来插入表达式的值。
val name = "Alice"
val age = 18
println(s"My name is $name and I am $age years old.")

这些是 println 函数的一些常见用法。你可以根据需要使用不同的方式来格式化输出。

集合

在Scala中,集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable,所以Scala中的集合都可以使用 foreach方法。在Scala中集合有可变(mutable)和不可变(immutable)两种类型。

List

如我们可以使用如下方式定义一个List,其他集合类型的定义方式也差不多。

object Main {
  def main(args: Array[String]): Unit = {
    // 定义一个空的字符串列表
    var emptyList: List[String] = List()
    // 定义一个具有数据的列表
    var intList = List(1, 2, 3, 4, 5, 6)
    // 定义空列表
    var emptyList2 = Nil
    // 使用::运算符连接元素
    var numList = 1 :: (2 :: (3 :: Nil))
    println(emptyList)
    println(intList)
    println(emptyList2)
    println(numList)
  }
}
输出:
List()
List(1, 2, 3, 4, 5, 6)
List()
List(1, 2, 3)

下面是一些List的常用方法:

val list = List(1, 2, 3, 4)
// 获取列表的长度
val length = list.length
// 获取列表的第一个元素
val first = list.head
// 获取列表的最后一个元素
val last = list.last
// 获取列表除第一个元素外剩余的元素
val tail = list.tail
// 获取列表除最后一个元素外剩余的元素
val init = list.init
// 反转列表
val reversed = list.reverse
// 在列表头部添加元素
val newList1 = 0 +: list
// 在列表尾部添加元素
val newList2 = list :+ 5
// 连接两个列表
val list1 = List(1, 2)
val list2 = List(3, 4)
val concatenatedList = list1 ++ list2
// 检查列表是否为空
val isEmpty = list.isEmpty
// 检查列表是否包含某个元素
val containsElement = list.contains(1)
// 过滤列表中的元素
val filteredList = list.filter(_ > 2)
// 映射列表中的元素
val mappedList = list.map(_ * 2)
// 折叠列表中的元素(从左到右)
val sum1 = list.foldLeft(0)(_ + _)
// 折叠列表中的元素(从右到左)
val sum2 = list.foldRight(0)(_ + _)
// 拉链操作
val names = List("Alice", "Bob", "Charlie")
val ages = List(25, 32, 29)
val zipped = names.zip(ages) // List(("Alice", 25), ("Bob", 32), ("Charlie", 29))
// 拉链操作后解压缩
val (unzippedNames, unzippedAges) = zipped.unzip // (List("Alice", "Bob", "Charlie"), List(25, 32, 29))

更多方法不再赘述,网上很容易查阅到相关文章。

Map

object Main {
  def main(args: Array[String]): Unit = {
    // 定义一个空的映射
    val emptyMap = Map()
    // 定义一个具有数据的映射
    val intMap = Map("key1" -> 1, "key2" -> 2)
    // 使用元组定义一个映射
    val tupleMap = Map(("key1", 1), ("key2", 2))
    println(emptyMap)
    println(intMap)
    println(tupleMap)
  }
}
输出:
Map()
Map(key1 -> 1, key2 -> 2)
Map(key1 -> 1, key2 -> 2)

下面是map常用的一些方法:

val map = Map("key1" -> 1, "key2" -> 2)
// 获取映射的大小
val size = map.size
// 获取映射中的所有键
val keys = map.keys
// 获取映射中的所有值
val values = map.values
// 检查映射是否为空
val isEmpty = map.isEmpty
// 检查映射是否包含某个键
val containsKey = map.contains("key1")
// 获取映射中某个键对应的值
val value = map("key1")
// 获取映射中某个键对应的值,如果不存在则返回默认值
val valueOrDefault = map.getOrElse("key3", 0)
// 过滤映射中的元素
val filteredMap = map.filter { case (k, v) => v > 1 }
// 映射映射中的元素
val mappedMap = map.map { case (k, v) => (k, v * 2) }
// 遍历映射中的元素
map.foreach { case (k, v) => println(s"key: $k, value: $v") }

这里的case关键字起到匹配的作用。

Range

Range属于序列(Seq)这一类集合的子集。它表示一个整数序列,可以用来遍历一个整数区间内的所有整数。例如,1 to 5表示一个从1到5的整数序列,包括1和5。

Range常见于for循环中,如下可定义一个Range:

// 定义一个从1到5的整数序列,包括1和5
val range1 = 1 to 5
// 定义一个从1到5的整数序列,包括1但不包括5
val range2 = 1 until 5
// 定义一个从1到10的整数序列,步长为2
val range3 = 1 to 10 by 2
// 定义一个从10到1的整数序列,步长为-1
val range4 = 10 to 1 by -1

如果我们想把Range转为List,我们可以这样做:

val range = 1 to 5
val list = range.toList

Range继承自Seq,因此它拥有Seq的所有常用方法,例如lengthheadlasttailinitreverseisEmptycontainsfiltermapfoldLeftfoldRight等。它还拥有一些特殊的方法,例如:

val range = 1 to 10 by 2
// 获取序列的起始值
val start = range.start
// 获取序列的结束值
val end = range.end
// 获取序列的步长
val step = range.step
// 获取一个包括结束值的新序列
val inclusiveRange = range.inclusive
相关实践学习
简单用户画像分析
本场景主要介绍基于海量日志数据进行简单用户画像分析为背景,如何通过使用DataWorks完成数据采集 、加工数据、配置数据质量监控和数据可视化展现等任务。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps 
目录
相关文章
|
4月前
|
大数据 Scala
大数据生态思维导图____2021最新最全Scala语法思维导图!(待更新)
大数据生态思维导图____2021最新最全Scala语法思维导图!(待更新)
19 0
|
8月前
|
安全 大数据 Java
【大数据】初识Scala 4
【大数据】初识Scala
76 0
|
8月前
|
大数据 Java Scala
【大数据】初识Scala 3
【大数据】初识Scala
51 0
|
8月前
|
分布式计算 Java 大数据
【大数据】初识Scala 1
【大数据】初识Scala
63 0
|
11月前
|
存储 大数据 Scala
大数据开发基础的编程语言的Scala的Actor编程
当谈到大数据开发时,Scala是一个非常流行的编程语言。Scala是一种静态类型的编程语言,它结合了面向对象和函数式编程范型。Scala为大数据处理提供了强大的支持,因此在许多大数据项目中被广泛使用。其中,Scala的Actor编程模型可以帮助我们设计和实现高效的并发系统。
71 0
|
11月前
|
Java 大数据 Scala
大数据开发基础的编程语言的Scala的基础语法
Scala是一种基于JVM的多范式编程语言,它支持面向对象编程和函数式编程,并具有强大的类型推导机制和静态类型检查。本文将介绍Scala的基础语法,帮助开发者快速入门这门语言。
58 0
|
11月前
|
安全 Java 大数据
大数据开发基础的编程语言的Scala的类型系统
Scala是一种强类型的编程语言,它具有一套完善的类型系统。本文将介绍Scala的类型系统,帮助开发者了解这门语言的类型安全性和灵活性。
73 0
|
Java 大数据 Scala
大数据开发基础的编程语言的Scala的类和对象
Scala是一种基于JVM的编程语言,它支持面向对象编程和函数式编程。本文将介绍Scala中类和对象的概念和用法,帮助开发者更好地理解和应用这门语言。
48 0
|
大数据 Serverless Scala
大数据开发基础的编程语言的Scala的函数和闭包
Scala是一种支持函数式编程的编程语言,它具有强大的函数和闭包功能。本文将介绍Scala中函数和闭包的概念和用法,帮助开发者更好地理解和应用这门语言。
55 0
|
大数据 Java Scala
大数据Scala系列之模式匹配和样例类
大数据Scala系列之模式匹配和样例类1.样例类在Scala中样例类是一中特殊的类,样例类是不可变的,可以通过值进行比较,可用于模式匹配。定义一个样例类:1.构造器中每一个参数都是val,除非显示地声明为var 2.
828 0

热门文章

最新文章