Scala 第一篇 基础篇

发布于:2024-04-17 ⋅ 阅读:(142) ⋅ 点赞:(0)

Scala 第一篇 基础篇

一、变量与常量

1、变量

创建变量:必须赋初值
1、可以不指定变量的具体类型如 var a = 5,则变量的类型由【首次】赋值的值类型决定且锁定。
2、若声明时已指定变量类型如 var a:String = “”,则类型不可更改

var variableName[:Type] = V

2、常量

创建常量且初始化后,常量的类型和值都将锁定

val valName[:Type] = V

二、数据类型

1、数据基本类型概览

Any: 所有类型的超类(顶级类型)
AnyVal: 值类型的超类
AnyRef: 引用类型的超类
Unit: 无值,类似java中的void
Nothing: 所有类型的子类
Null: 表示null或空引用

在这里插入图片描述

2、元组的声明与使用

元组 TupleN[T1,…,TN] (复合类型)
T :泛型
N :1~22
():表示元组
元组类型表达:推荐 syntactic suger 语法糖
(String, Int) <=> Tuple2[String, Int]

二元组的声明和初始化

val tp2 = ("java", 88)	// 实际应用:最简语法
val tp2: (String, Int) = ("java", 88) // 初学推荐
val tp2: Tuple2[String,Int] = Tuple2("java", 88) //写全
val v1 = tp2._1		// 元组的使用,提取元组第一个值
val v2 = tp2._2
val it:Iterator[Any] = tp2.productIterator

注意:为避免冲突,须知()还可以表示:

  1. 数组下标提取 array(int)
  2. 方法的参数列表 method(p1,…,p2)

3、Range介绍和使用

Range 属于值类型的超类 AnyVal,表示一个连续的数字序列。
通过 to 方法或 until 方法创建

// >=1 && < 10
val r:Range = START until END by STEP 	// STEP缺省默认为1
val a:Range = 1 until 10 by 2			// 创建了一个半开范围,从 1 开始(包含 1),到 10 结束(不包含 10),步长为 2。
val r:Range = Range(start:Int,stop:Int,step:Int)

// >=1 && <=10
val r:Range = START to END by STEP 		// STEP缺省默认为1
val a:Range.Inclusive = 1 to 10 by 2	// 创建了一个闭合范围,从 1 开始(包含 1),到 10 结束(包含 10),步长为 2。
val a:Range.Inclusive = Range.Inclusive(start:Int,stop:Int,step:Int)

4、Option 类型的使用和设计

Option 是 Scala 中用来表示可能包含值或者不包含值的容器类型。通常用于避免空指针异常,因为它要求显式地处理可能为空的情况。

val opt: Option[Int] = Some(10) //将 opt 设置为包含整数值 10 的 Some
val rst = opt.getOrElse(0)
//使用 getOrElse 方法从 opt 中获取值,如果 opt 是 Some,则返回其中的值(这里是 10),如果是 None,则返回指定的默认值(这里是 0)。

函数的设计利用 Scala 中的 Option 类型,以一种更安全和更函数式的方式处理可能为空的情况。通过返回 Option 类型,明确地表达了可能存在结果和不存在结果的两种可能性,从而避免空指针异常等问题。

def add(a:Int,b:Int):Option[Int] = {
    val sum = a + b
    if(sum>100){
    	None
    }else{
    	Some(sum)
	}
}

5、类型别名

简化复杂的类型名称

type S = String		//用S代表String
var str:S = "henry"

三、运算符

跟Java,C++等没什么区别

赋值运算符: 	= += -= *= /= %=
算术运算符: 	+ - * / %			没有 ++ --
关系运算符: 	> >= < <= == !=
逻辑运算符: 	&& || !

四、程序逻辑

1、一切都是表达式

val expr = {}		//声明时执行
lazy val expr = {}	//惰性表达式  使用时执行

惰性表达式举例

// 定义一个类,表示从数据库加载的数据
class DatabaseData {
  // 模拟从数据库加载数据的耗时操作
  println("Loading data from database...")
  // 假设这里是实际的加载数据操作
  // 这里只是简单地打印一条消息表示加载完成
  println("Data loaded.")
}

// 创建一个对象,包含一个惰性求值的表达式
object Main {
  // 定义一个惰性求值的表达式,表示从数据库加载数据
  lazy val data = new DatabaseData

  def main(args: Array[String]): Unit = {
    println("Main method starts.")
    // 在主方法中使用数据
    println("Accessing data...")
    // 当访问 data 时,如果 data 还没有被初始化,则执行初始化操作
    println(data)
    // 再次访问 data,此时由于已经初始化过了,所以不会再次执行初始化操作
    println(data)
    println("Main method ends.")
  }
}

2、分支语句

if...else 语句
val x = 10
//正常写法
if (x > 10) {
  println("x 大于 10")
} else if (x < 10) {
  println("x 小于 10")
} else {
  println("x 等于 10")
}
//类三元运算符写法
val rst = if(x > 10) "x 大于 10" else "x 小于等于 10"
模式匹配(重点)

在 Scala 中,模式匹配中的条件守卫允许在模式匹配的基础上增加额外的条件判断。当模式匹配成功后,会检查守卫条件是否为真,只有条件为真时,才会执行相应的逻辑

  1. 数值

    val dial = Random.nextInt(4)
    val rst = dial match {
    	case 0 => "余额"
        case 1 => "充值"
        case 2 => "活动"
        case 3 => "贷款"
        case _ => "未知"
    }
    
  2. 字符串与条件守卫

    val content = "123456@qq.com"
    val rst = content match {
        case a if a.matches("\\w+@\\w{2,}\\.(com|cn)") => "email"
        case a if a.matches("1[3-9]\\d{9}") => "handset"
        case "NULL" => "nothing"
        case _ => "unknown"
    }
    println(rst)	//输出:email
    
    val day = "Monday"
    day match {
      case "Monday" => println("星期一")	//最后只调用此行输出:星期一
      case "Tuesday" => println("星期二")
      case "Wednesday" => println("星期三")
      case _ if day.startsWith("T") => println("星期二到星期五")
      case _ => println("其他")
    }
    
  3. 元组

    val rst = (7,101) match {
        case (3,_) => (3,1)
        case (5,a) => if(a%2==0) (5,0) else (5,1)
        case _ => (0,0)
    }
    
  4. 列表

    val rst = List(1,"hive",88) match {
        case List(id,"java",score:Int) => if(score>=60) ("java","pass") else ("java","fail")
        case _ => ("SKIP","NONE")
    }
    
  5. 类型

    val rst = value match {
        case a:Int => ("int",a)
        case a:(Int,Int) => ("Tuple2",a)
        case a:String => ("String",a)
        case _ => ("Unknown",1)
    }
    
  6. 嵌套

    val rst = (1,("java",67)) match {
        case (1,(sub:String,score:Int)) => (sub,score)
        case _ => ("UnknownFormat",1)
    }
    

3、循环语句

while循环
var i = 0
while (i < 5) {
  println(s"当前的值为: $i")
  i += 1
}
do while循环
var j = 0
do {
  println(s"当前的值为: $j")
  j += 1
} while (j < 5)

for 循环

有多种不同的用法和语法糖

// 基本的 for 循环
for (i <- 0 until 5) {
  println(s"当前的值为: $i")
}

// 带有条件的 for 循环
for (i <- 0 until 10 if i % 2 == 0) {
  println(s"偶数值为: $i")
}

// 嵌套的 for 循环
for (i <- 0 to 2; j <- 0 to 2) {
  println(s"($i, $j)")
}

五、集合

scala 支持运算符的重载
immutable 不可变集合包,mutable 可变集合包
默认导入不可变集合包
使用可变集合需要先import scala.collection.mutable,再通过 mutable.T 使用
常用集合:List、Set、Map、Array

import scala.collection.immutable.Set						// 导入具体类
import scala.collection.mutable._							// 导入包内所有类
import scala.collection.mutable.{ListBuffer,ArrayBuffer}	// 导入包内部分类

1、List

import scala.collection.immutable.List
import scala.collection.mutable.LiscalastBuffer //可以代替链表高效插删

List分组体验代码

val list = List(1, 2, 3, 4, 5, 6, 7)

val it: Iterator[List[Int]] = list.grouped(3)
println("按3个分组")
it.foreach(println)

println("滑动窗口,步长1")
val it2 = list.sliding(3, 1)
it2.foreach(println)

println("滑动窗口,步长2")
val it3 = list.sliding(3, 2)
it3.foreach(println)

println("排列组合,不考虑顺序问题")
val it4 = list.combinations(3)
it4.foreach(println)

println("分组,按模2不同分组")
val map = list.groupBy(a => a % 2)
map.foreach(println)

println("是否大于3分组")
val map2 = list.groupBy(a => a > 3)
map2.foreach(println)

输出结果

3个分组
List(1, 2, 3)
List(4, 5, 6)
List(7)
滑动窗口,步长1
List(1, 2, 3)
List(2, 3, 4)
List(3, 4, 5)
List(4, 5, 6)
List(5, 6, 7)
滑动窗口,步长2
List(1, 2, 3)
List(3, 4, 5)
List(5, 6, 7)
排列组合,不考虑顺序问题
List(1, 2, 3)
List(1, 2, 4)
List(1, 2, 5)
List(1, 2, 6)
List(1, 2, 7)
List(1, 3, 4)
List(1, 3, 5)
List(1, 3, 6)
List(1, 3, 7)
List(1, 4, 5)
List(1, 4, 6)
List(1, 4, 7)
List(1, 5, 6)
List(1, 5, 7)
List(1, 6, 7)
List(2, 3, 4)
List(2, 3, 5)
List(2, 3, 6)
List(2, 3, 7)
List(2, 4, 5)
List(2, 4, 6)
List(2, 4, 7)
List(2, 5, 6)
List(2, 5, 7)
List(2, 6, 7)
List(3, 4, 5)
List(3, 4, 6)
List(3, 4, 7)
List(3, 5, 6)
List(3, 5, 7)
List(3, 6, 7)
List(4, 5, 6)
List(4, 5, 7)
List(4, 6, 7)
List(5, 6, 7)
分组,按模2不同分组scala
(1,List(1, 3, 5, 7))
(0,List(2, 4, 6))
是否大于3分组
(false,List(1, 2, 3))
(true,List(4, 5, 6, 7))

2、Set

import scala.collection.immutable.Set
import scala.collection.mutable.Set

Set 体验差&~、交&、并| 代码

// 可变集合
val set1 =  mutable.Set(1, 2, 3)
val set2 = mutable.Set(3, 4, 5)
// 不可变集合
val set3 = Set(3, 4, 5, 6)

println("交集")
val ints0 = set1 & set2      // 等价于set1.intersect(set2)
println(ints0)

println("并集")
val ints1 = set1 | set2     // 等价于set1.union(set2)
println(ints1)

println("差集")
val ints2 = set1 &~ set2    // 等价于set1.diff(set2)
println(ints2)scala

// 差集,前后影响数据类型
val ints3: mutable.Set[Int] = set1 &~ set3
val ints4: Set[Int] = set3 &~ set1

输出结果

交集
Set(3)
并集
Set(1, 5, 2, 3, 4)
差集
Set(1, 2)

3、Map

import scala.collection.immutable.Map
import scala.collection.mutable.Map

Map体验 += -= ++ 代码

// 创建一个可变的 Map
var map = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)

// 添加键值对
map += ("d" -> 1)
map += (("sss", 2), ("xxx", 3))
scala
// 移除键值对
map -= "sss"

// 使用 ++ 添加另一个 Map
map ++= Map("e" -> 5, "f" -> 6)
scala
// 输出结果
println(map)

输出结果

Map(e -> 5, xxx -> 3, b -> 2, d -> 1, a -> 1, c -> 3, f -> 6)

4、Array

import scala.Array
import scala.collection.mutable.ArrayBuffer

Array体验 += -= ++ 代码

// 创建一个可变的数组缓冲
val buffer = ArrayBuffer(1, 2, 3)

// 在尾部添加一个元素
buffer += 4

// 移除指定位置的元素
buffer -= 2scala

// 添加另一个数组缓冲的所有元素
buffer ++= ArrayBuffer(5, 6, 7)scala

// 输出结果
println(buffer)

输出结果

ArrayBuffer(1, 3, 4, 5, 6, 7)

备注:Array 和 List 是两种不同的集合类型,区别如下

  1. 可变性
  • Array 是可变的,可以修改其元素的值,并且可以改变数组的长度。
  • List 是不可变的,一旦创建后,就不能修改其元素或长度。如果需要添加、删除或修改元素,可以通过创建新的 List 实例来实现。
  1. 实现方式
  • Array 在底层是通过 Java 数组来实现的,它提供了快速的随机访问(通过索引)和可变性。
  • List 则是使用链表(Linked List)来实现的,每个元素都连接到下一个元素,因此访问和修改操作的效率较低,但对于添加和删除元素来说更加高效。
  1. 类型
  • Array 是一个包含固定大小元素的集合,元素类型可以是任意类型,甚至可以是不同类型。
  • List 是一个包含有序元素的集合,元素的类型相同,并且列表的长度可以动态增长或缩减。

六、函数

1、普通函数, 匿名函数

普通函数:最常见的函数类型
匿名函数:也称为函数字面量或 Lambda 表达式,没有名称,直接定义在代码中

函数定义
// 普通函数 
def add(x: Int, y: Int): Int = {
  x + y
}
// 匿名函数
val add: (Int, Int) => Int = (x, y) => x + y
字符串插值函数

字符串插值函数属于 Scala 中的内置函数
字符串插值函数允许将变量、表达式或者其他字符串插入到字符串字面量中,形成一个新的字符串。
Scala 提供了三种常用的字符串插值函数

val str = s""			
val str = raw""			
val str = f""

s 插值器:用于字符串插值,允许在字符串中引用变量或表达式。

val name = "Alice"
val age = 30
val message = s"My name is $name and I am $age years old."
println(message)		// 输出:My name is Alice and I am 30 years old.

f 插值器:用于格式化字符串,类似于 C 语言中的 printf 函数。

val weight = 75.5
val bmi = f"$weight%.2f"
println(s"Your BMI is $bmi")	//输出:Your BMI is 75.50

raw 插值器:用于原始字符串,不会对字符串中的特殊字符进行转义。

val path = raw"C:\Users\node"
println(path)	//输出:C:\Users\node

2、高阶函数(重点)

接受函数作为参数或返回函数的函数。

def applyFunc(f: Int => Int, x: Int): Int = f(x)
柯里化函数

将多个参数列表转换为一系列函数调用,每次调用返回一个新的函数

def add(x: Int)(y: Int): Int = x + y

分析以下两个方法的区别

def func1(init:Int)(a:Int*):Int = init+a.sum 	// 函数作为返回类型
def func2(init:Int, a:Int*):Int = init+a.sum

func1func2 实现的功能是相同的,只是在参数列表的形式上有所不同。使用柯里化的形式可以使代码更加灵活,提高了函数的复用性和可读性。

函数作为参数传递的三种方式
val it = List(1, 2, 3, 4, 5, 6, 7)
def show(e:Int)=println(e)
it.foreach(show) 			// 【自定义】函数(重用) 		3
it.foreach(e=>println(e)) 	// 【自定义】函数(一次性)	   2
it.foreach(println) 		// 调用scala【已定义】函数	1

3、隐式参数,隐式函数

隐式函数作用:共用,简化
不建议使用太多,了解即可

场景一

// 当编译器在某个上下文中需要类型 Int 而实际提供的是类型 String 时,会查找适用的隐式转换函数并将字符串转换为整数。
implicit def str(a:String) = a.toInt

val a: Int = "100"
val sum: Int = a + 1
println(sum)

场景二

def sum(array: Array[Int])(implicit a: Int = 0) = if (array.sum < 0) a else array.sum

println(sum(Array(1, 2)))			// 3
println(sum(Array(1, 2, -4)))		// 0
println(sum(Array(1, 2, -4))(100))	// 100
def sum(array: Array[Int])(implicit a: Int) = if (array.sum < 0) a else array.sum
	
implicit val st: Int = 1000
println(sum(Array(1, 2, -4)))		// 1000

4、偏函数

只对输入的某些值定义的函数,而对其他值不进行定义。

// 定义一个偏函数,只处理偶数的情况
val evenNumber: PartialFunction[Int, String] = {
  case x if x % 2 == 0 => s"$x is even"
}

// 使用偏函数处理输入值
println(evenNumber(2))  // 输出 "2 is even"
println(evenNumber(3))  // 抛出 MatchError 异常,因为偏函数未定义输入值为奇数的情况

5、总结

一、命名参数:
1、理解:默认入参规则是按方法或函数的参数列表顺序传参
2、应用:按 “参数名=值” 格式可以不遵循原始参数列表顺序传参

二、参数缺省值(默认值)
1、语法:声明方法时可以通过类似 (a:Int=5,…) 形式给参数提供【默认值】
2、应用:带有默认值的参数,若需要传入的就是默认值,则可缺省

三、匿名函数:将函数作为方法参数传递
1、语法:(参数列表)=>{方法体}

四、高阶函数
1、现象:将函数作为方法参数或返回类型
2、应用:函数作为返回类型:柯里化

五、中置表达式
1、语法:var v = 左元素 函数 右元素
2、案例:var range = 1 to|until 10
3、注意:方法的参数【只有一个参数】才可以使用中置表达式

六、嵌套函数
1、语法:允许在一个函数内部声明一个函数,作用域限当前函数内部

七、隐式参数
1、语法:参数前添加 implicit 关键字
2、限制:只能是最后一个参数列表且独占一个参数列表,自动传入同类型隐式值,通常和柯里化配合使用

八、隐式函数
1、语法:声明函数时,在 def 前添加 implicit 关键字
2、使用:类型转换,类型增强

implicit def toInt(str:String)=str.toInt
val b:Int = "123"	// 隐式转换

implicit def bool(bool:Boolean) = if(bool) 1 else 0
val a:Int = false

九、闭包
1、概念:在函数内使用函数外定义的自由变量,则该函数即闭包
2、例如:

var a = 5
val add = (b:Int) => a+b

3、闭包能够捕获自由变量的变化,闭包对自由变量做出的改变闭包外亦可见(惰性处理:即函数执行后产生作用)

6、补充:下划线 _ 用途。

可以用于表示各种不同的语法元素,具体取决于上下文

通配符:在模式匹配、参数列表、导入语句等中,下划线 _ 可以用作通配符,表示匹配任意值或任意参数。

val list = List(1, 2, 3)
list.foreach(_ => println("Hello")) // 使用下划线作为参数,表示忽略参数

占位符:在部分应用函数、匿名函数等场景中,下划线 _ 可以作为占位符,表示一个参数的位置,类似于匿名函数中的参数。

val addOne: Int => Int = _ + 1	// 使用下划线作为参数占位符,表示参数
import package._				// 导全部包
case _							// default

部分应用函数:在柯里化函数中,下划线 _ 可以用作占位符,表示对某个参数进行部分应用,生成一个新的函数。

def add(a: Int)(b: Int): Int = a + bscala
val addTwo: Int => Int = add(2)(_) // 使用下划线作为部分应用函数的占位符

// 基于下划线的简易传参语法:参数占位符,参数列表中每个参数可以使用一条下划线
def cal(func:(Int,Int)=>Int, a:Int*)={
    var sum = 0
    for (elem <- a) {
        sum = func(sum, elem)
    }
    sum
}
// 下面两种写法的效果相同
val rst = cal(_ + _, 1, 2, 3, 4, 5)
val rst = cal((a,b)=>a+b, 1, 2, 3, 4, 5)

占位符语法糖:在某些情况下,下划线 _ 可以用作语法糖,简化代码书写。

val list = List(1, 2, 3)
val sum = list.reduce(_ + _) // 使用下划线作为占位符,简化参数的书写

七、数组方法(重点)

且听下回分析


网站公告

今日签到

点亮在社区的每一天
去签到