当前位置: 首页 > news >正文

scala中for推导式详细讲解

一、scala-for推导式概念解读

想象一下:你要做一道新菜

普通的 for 循环,就像一个只会严格执行命令的机器人:
“第一步:拿一个苹果。”
“第二步:切开。”
“第三步:装盘。”

它只是按部就班地做事。

for 推导式,就像一本**“智能菜谱”,它不是告诉你一步步做什么,而是描述你最终想要一个什么样的“新东西”**。

这本智能菜谱有三个核心部分:

  1. 食材来源 (Generator):从哪里拿原材料。
  2. 筛选条件 (Filter/Guard):只挑选符合条件的原材料。
  3. 加工方法 (yield):把选好的原材料加工成一道新菜。

1. 最简单的推导式:加工所有食材

假设你有一篮子数字,你想要一个“新的篮子”,里面装的是原来每个数字的两倍。

食材来源nums = List(1, 2, 3, 4)
加工方法:把每个数字 n 变成 n * 2

val nums = List(1, 2, 3, 4)// 智能菜谱写法
val doubledNums = for (n <- nums) yield n * 2println(doubledNums) // 输出: List(2, 4, 6, 8)

通俗解释

  • for (n <- nums)“对于 nums 篮子里的每一个东西,我们暂时叫它 n。这里的 <- 符号可以读作 “in” 或者 “来自”。
  • yield n * 2“请把 n * 2 这个新东西,放进我最终想要的新篮子里”yield 是“产出”的意思,是 for 推导式的灵魂。只要看到 yield,就说明它不是一个简单的循环,而是在构建一个全新的集合

2. 加入筛选条件:只挑选想要的食材

现在,我们只想加工篮子里的偶数。

食材来源nums = List(1, 2, 3, 4)
筛选条件:只选偶数,即 n % 2 == 0
加工方法:把选出来的偶数 n 变成 n * 2

val nums = List(1, 2, 3, 4)// 加入 if 条件
val doubledEvens = for (n <- nums if n % 2 == 0) yield n * 2println(doubledEvens) // 输出: List(4, 8)

通俗解释

  • if n % 2 == 0:这就是一个“过滤器”或“守卫”。它告诉菜谱:“只有当 n 是偶数时,才对它进行下一步的 yield 加工,否则就直接跳过这个 n”。

3. 使用多种食材:制作组合菜

这是 for 推导式最强大的地方。假设你有两种食材,你想把它们两两组合,创造出所有可能的搭配。

食材来源1 (字母)letters = List("A", "B")
食材来源2 (数字)numbers = List(1, 2)
加工方法:把字母 l 和数字 n 组合成一个新字符串,比如 "A1"

val letters = List("A", "B")
val numbers = List(1, 2)// 组合所有可能
val combinations = for {l <- lettersn <- numbers
} yield l + nprintln(combinations) // 输出: List("A1", "A2", "B1", "B2")

通俗解释

  • 这个推导式读起来就像:“对于每一个来自 letters 的字母 l,请再遍历一遍 numbers 里的所有数字 n,然后产出 l + n 这个组合”。
  • 它非常清晰地表达了“组合所有可能性”的意图,比写两层嵌套的 for 循环要优雅得多。

总结:for 推导式是什么?

它根本就不是一个循环,而是一个集合(或其它容器)的“构造器”或“转换器”

它让你用一种非常接近自然语言的方式,来描述你想要生成一个什么样的新集合,而不需要你自己去写循环、判断、创建新列表、添加元素这些繁琐的步骤。编译器会把这个漂亮的“智能菜谱”自动翻译成背后更复杂的代码(map, flatMap, filter 等操作)。

所以,当你下次想对一个列表进行转换、筛选、或者组合来得到一个新列表时,就想想这本“智能菜谱”吧!


二、返回的结果类型

for 推导式产出的集合类型,通常取决于你第一个 “食材来源” (generator) 的类型

可以把它理解成一个“从哪里来,回哪里去”的原则。你从一个 List 开始,它就给你返回一个新的 List;你从一个 Set 开始,它就给你返回一个新的 Set

下面我们来看几个例子,你就彻底明白了。


1. 从列表(List)出发,得到列表(List)

这是我们之前看到的例子,也是最常见的情况。

val numbers = List(1, 2, 3)
val result = for (n <- numbers) yield n * 2
// numbers 是 List, 所以 result 也是 List
println(result) // 输出: List(2, 4, 6)
println(result.getClass) // 输出: class scala.collection.immutable.List

2. 从集合(Set)出发,得到集合(Set)

Set 是一个不允许有重复元素的集合。for 推导式会很智能地保持这个特性。

val numbers = Set(1, 2, 3, 2, 1) // Set 会自动去重,实际内容是 {1, 2, 3}
val result = for (n <- numbers) yield n * 2
// numbers 是 Set, 所以 result 也是 Set
println(result) // 输出: Set(2, 4, 6)
println(result.getClass) // 输出: class scala.collection.immutable.HashSet

3. 从字符串(String)出发,得到字符串(String)

这是一个很有趣的例子!在 Scala 中,字符串可以被看作是字符(Char)的集合。

val text = "abc"
val result = for (char <- text) yield char.toUpper
// text 是 String, 所以 result 也是 String
println(result) // 输出: ABC
println(result.getClass) // 输出: class java.lang.String

4. 从范围(Range)出发,得到向量(Vector)

Range 是一种特殊的表示数字序列的集合。对它进行推导,通常会得到一个更通用的序列类型 VectorVector 你可以暂时就把它当成一个性能很好的 List

val numbers = 1 to 3 // 这是一个 Range,包含 1, 2, 3
val result = for (n <- numbers) yield n * 2
// numbers 是 Range, 但结果是一个 Vector
println(result) // 输出: Vector(2, 4, 6)
println(result.getClass) // 输出: class scala.collection.immutable.Vector

5. 从映射(Map)出发,得到映射(Map)

如果你从一个 Map(键值对集合)出发,并且 yield 回一个新的键值对,那么你就会得到一个新的 Map

val scores = Map("Alice" -> 10, "Bob" -> 8)// yield 一个 (key -> value) 格式的元组
val result = for ((name, score) <- scores) yield (name.toUpperCase -> (score * 10))// scores 是 Map, yield 的是键值对, 所以 result 也是 Map
println(result) // 输出: Map("ALICE" -> 100, "BOB" -> 80)
println(result.getClass) // 输出: class scala.collection.immutable.Map$Map2

总结与技巧

  1. 同类型返回原则for 推导式非常智能,它会尝试返回与源集合最匹配的类型。List -> List, Set -> Set, String -> String

  2. 我想要指定类型怎么办?:如果你想把推导的结果强制变成某个特定的集合类型,非常简单,在表达式的最后调用 .toXxx 方法即可。

    val text = "hello"
    // 我希望结果是一个 List, 而不是 String
    val resultAsList = (for (c <- text) yield c.toUpper).toList
    println(resultAsList) // 输出: List(H, E, L, L, O)
    

所以,for 推导式远比一个简单的循环要强大,它深度整合了 Scala 强大的集合库,让代码既简洁又符合逻辑。

http://www.dtcms.com/a/390015.html

相关文章:

  • React学习 ---- 基础知识学习
  • C语言实现MATLAB中的Fir1带通滤波器
  • 微信小程序开发教程(十七)
  • 9月18日星期四今日早报简报微语报早读
  • SqlSugar 问题记录
  • 记一次宝塔+nginx+php8+thinkphp8多应用下某个应用报错404的问题 - nginx、php日志全无 - 无法追踪
  • Windows Server远程桌面(RDP)安全优化
  • 工具链过于分散会导致哪些问题
  • 【RAG】Youtu-GraphRAG
  • 惠普LaserJet Pro M203dn黑白激光打印机双面卡纸维修一例
  • 专题二 二叉树中的深度优先搜索
  • Git 多人协作(1)
  • 设计模式第三章(迭代器模式)
  • 网络原理(4):HTTP协议 -- HTTP请求 -- 首行(请求方法)
  • 密钥下发服务中心:双重验证 + 实时监控的轻量级密钥管理解决方案
  • 硬件 - RK3588部分(4) - 原理图 - RK806
  • Sass开发【三】
  • 百度之星2025(第二场)
  • Ovis-U1:阿里巴巴推出的统一的多模态理解与生成模型
  • 深入剖析C++智能指针:unique_ptr与shared_ptr的资源管理哲学
  • 创建索引失败,表一直查询不了
  • 知识分享:网线和DB9正确接线方法
  • 【算法笔记】前缀树
  • 让ai完成原神调酒 试做
  • 第十四届蓝桥杯青少组C++选拔赛[2022.11.27]第二部分编程题(2、拼写单词)
  • 私有化部署UE像素流后,通过实时云渲染平台配置网络端口,实现云推流内网及公网访问
  • Day 05 Geant4多线程 Multithreading --------以B1为例
  • 【word解析】从 Word 提取数学公式并渲染到 Web 页面的完整指南
  • FreeRTOS 队列机制详解:阻塞、唤醒与任务同步
  • Unity学习之UI优化总结