news 2026/9/15 3:09:55

深入掌握 Scala 核心语法:函数、控制流、模式匹配与面向对象速查指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入掌握 Scala 核心语法:函数、控制流、模式匹配与面向对象速查指南

深入掌握 Scala 核心语法:函数、控制流、模式匹配与面向对象速查指南

【免费下载链接】reference面向开发者的技术速查清单(Cheat Sheets)集合,整理常见技术、工具与开发流程,帮助快速查阅关键信息,提高开发效率。项目地址: https://gitcode.com/GitHub_Trending/referen/reference

导读:本文以 docs/scala.md 速查表为骨架,系统梳理 Scala 语法中最容易踩坑的高频知识点——从函数定义、传名/传值调用、柯里化与匿名函数,到 for 表达式、模式匹配的解构与稳定标识符陷阱,再到构造器参数、特质与类型系统。读完本文,你将能够快速写出符合 Scala 惯用法的代码,同时避开「过程与函数混淆」「模式变量遮蔽」「错误类型转换」等常见陷阱,并清楚每一处语法在 Scala 官方仓库 与 JVM 生态中的定位。

一、入门基础:函数、变量、包与数据结构

1.1 函数定义的核心规则

Scala 是一门同时支持函数式与面向对象范式的语言,函数定义的第一条铁律就是等号(=)不可缺失

def f(x: Int) = { x*x } // ✅ GOOD 显式返回 x 的平方 def f(x: Int) { x*x } // ❌ BAD 定义函数,潜在风险: // 不加“=”号将会是一段返回Unit类型的过程, // 这将会导致意想不到的错误。

不加=的写法意味着该方法是一个过程(procedure),无论方法体内最后一个表达式是什么,返回值都被忽略并退化为Unit(相当于 C/Java 中的void)。这在 Java 开发者初学 Scala 时尤为常见——方法体即便写出了x*x,调用方拿到的也只是一个()。这也是 Scala 3 中已将「无等号过程语法」移除(仅保留def f(x: Int): Unit = ...)的原因,足以说明该写法在语言演化层面的不受欢迎程度。

第二条规定是每个参数都必须声明类型

def f(x: Any) = println(x) // ✅ GOOD def f(x) = println(x) // ❌ BAD 定义函数 语法错误: 每个参数都需要指定类型。

Scala 编译器在绝大多数场景下都能做类型推断,但方法参数(以及类构造参数)必须显式给出类型,这是类型安全与重载决议的前提。

1.2 类型别名与调用约定

type R = Double // 类型别名 def f(x: R) vs. def f(x: => R) // 传值调用 传名调用 (惰性参数)
  • def f(x: R)传值调用(by-value),参数在调用点先求值,再传入函数体,函数体内多次使用得到的是同一个已求值的结果。
  • def f(x: => R)传名调用(by-name),参数以「求值表达式」的形式传入,每次在函数体内使用时才会重新求值(惰性语义)。典型应用是自定义控制结构,例如标准库的whileif都是通过传名参数实现的。注意它不等同于缓存式惰性求值——传名参数每次使用都会重新计算。

1.3 匿名函数(Lambda)与占位符语法

(x:R) => x*x // 匿名函数 (1 to 5).map(_*2) vs. // 匿名函数: 下划线是参数的占位符 (1 to 5).reduceLeft( _+_ ) (1 to 5).map( x => x*x ) // 匿名函数: 必须命名以后才可以多次使用同一个参数
  • _是参数的占位符(placeholder)_*2等价于x => x*2_+_等价于(x,y) => x+y
  • 当同一个参数需要在表达式内出现多次时,占位符无法表达「同一个参数被复用」的语义,此时必须显式命名:map( x => x*x )

绑定中缀方法时的占位符书写顺序也需要注意:

(1 to 5).map(2*) // ✅ GOOD (1 to 5).map(*2) // ❌ BAD 匿名函数: 绑定中缀方法,明智的做法是2*_。

2*在 Scala 中会被解析为x => 2 * x(数字2作为接收者,*Int的方法,_是参数);反过来*2语法不合法。规范写法是2*_,显式表达「参数在方法之后」。

1.4 代码块风格与管道风格

(1 to 5).map { x => val y=x*2; println(y); y } // 匿名函数: 代码块风格,最后一个表达式作为返回值 (1 to 5) filter {_%2 == 0} map {_*2} // 匿名函数: 管道风格(或者用圆括号) // 匿名函数: 要传入多个代码块的话,需要使用花括号。 def compose(g:R=>R, h:R=>R) = (x:R) => g(h(x)) val f = compose({_*2}, {_-1})
  • 代码块风格map { x => ...; y }中,块内最后一个表达式y即返回值,块内可以声明局部val并产生副作用(如println)。
  • 管道风格(1 to 5) filter {...} map {...}省去了点号与圆括号,将集合处理串联为一条声明式管道,可读性极佳。
  • 多个参数块必须用花括号compose({_*2}, {_-1})这种「一个函数同时接收两个函数参数」的场景,花括号是必须的(圆括号会被当作单一参数元组)。

1.5 柯里化与偏函数

val zscore = (mean:R, sd:R) => (x:R) => (x-mean)/sd // 柯里化, 显然的语法。 def zscore(mean:R, sd:R) = (x:R) => (x-mean)/sd // 柯里化, 显然的语法。 def zscore(mean:R, sd:R)(x:R) = (x-mean)/sd // 柯里化,语法糖。然后:) val normer = zscore(7, 0.4) _ // 需要在尾部加下划线来变成偏函数(只对语法糖版本适用) def mapmakeT(seq: List[T]) = seq.map(g) // 泛型
  • 三种写法都是柯里化(currying):将「接收多个参数」的函数改写为「接收第一个参数、返回接收第二个参数的新函数」。
  • 前两种是「返回函数」的显式写法;第三种def zscore(mean:R, sd:R)(x:R)是**多参数列表(multiple parameter lists)**语法糖,编译器自动生成柯里化版本。
  • 偏函数应用(partial application)zscore(7, 0.4) _中尾部下划线将「已经传入两个参数、还差一个参数」的调用提升为函数值Double => Double,即固定均值 7、标准差 0.4 后的标准化函数。该语法仅对语法糖版本(多参数列表)适用。
  • 柯里化+泛型组合的mapmake[T]展示了参数列表拆分的另一个价值:第一组参数用于类型推断,第二组参数用于迭代

1.6 中缀语法与变长参数

5.+(3); 5 + 3 (1 to 5) map (_*2) // 中缀语法糖 def sum(args: Int*) = args.reduceLeft(_+_) // 变长参数
  • 5 + 3本质上是对Int.+方法的中缀调用,5.+(3)5 + 3完全等价,这是 Scala 运算符即方法(everything is a method)设计的直接体现。
  • (1 to 5) map (_*2)是方法调用的中缀糖,等价于(1 to 5).map(_*2)
  • args: Int*声明变长参数(varargs),在方法体内argsSeq[Int]形式存在,配合reduceLeft(_+_)可实现对任意数量参数的累加。

1.7 变量声明

var x = 5 // 可变变量 val x = 5 // ✅ GOOD x=6 // ❌ BAD 常量 var x: Double = 5 // 显式类型
  • val声明不可变引用(类似 Java 的final),是 Scala 的默认推荐;var声明可变引用。
  • val重新赋值(x=6)是编译错误,这保证了引用不会被意外替换,是函数式风格的基石。
  • 类型推断失效或需要向上转型时,可显式标注类型:var x: Double = 5Int字面量 5 会被自动拓宽为Double)。

1.8 包与导入

import scala.collection._ // 通配符导入 import scala.collection.Vector // 选择性导入 import scala.collection.{Vector, Sequence} // 重命名导入 import scala.collection.{Vector => Vec28} // 导入java.util包里除Date之外的一切 import java.util.{Date => _, _} // 文件开头的包名 pkg package pkg { ... } // 声明这是一个包
  • _通配符导入(Scala 2 语法;Scala 3 中推荐使用*),可导入一个包内的全部成员。
  • {Vector, Sequence}选择性导入多个类;{Vector => Vec28}重命名导入,用于解决命名冲突或简化书写。
  • {Date => _, _}是「隐藏某个成员 + 通配导入其余」的组合写法:先将Date重命名为下划线(即排除),再通配导入剩余全部。
  • package pkg { ... }声明花括号内的代码属于包pkg,用于在非文件头位置组织包结构。

1.9 常用数据结构

(1,2,3) // 元组字面量 (Tuple3) // 解构绑定:通过模式匹配来解构元组。 var (x,y,z) = (1,2,3) // ❌ BAD 潜在风险:整个元组被赋值给了每一个变量 var x,y,z = (1,2,3) // 列表 (不可变) var xs = List(1,2,3) xs(2) // 用括号索引 (slides) 1 :: List(2,3) // Cons(构成) 1 to 5 /* 等价于 => */ 1 until 6 // Range类型(语法糖) 1 to 10 by 2 // Unit类型的唯一成员 (相当于 C/Java 里的void) () (空括号)
  • (1,2,3)Tuple3[Int,Int,Int]的元组字面量,用于承载「小规模的异构数据组合」。
  • 解构绑定var (x,y,z) = (1,2,3)通过模式匹配把元组拆开,x=1, y=2, z=3;而var x,y,z = (1,2,3)危险写法——三个变量各自拿到整个元组,与直觉完全相反,务必避免。
  • List(1,2,3)是不可变列表,xs(2)用圆括号索引(Scala 的apply语法糖)。
  • ::Cons 操作符1 :: List(2,3)在头部插入元素得到List(1,2,3)
  • 1 to 5等价于1 until 6:前者包含上界生成1,2,3,4,5,后者排除上界生成1,2,3,4,5之外的1..5前五个。1 to 10 by 2以步长 2 生成1,3,5,7,9,底层都是Range类型的语法糖。
  • ()Unit类型的唯一实例,用于表示「无返回值」的副作用操作。

二、控制结构:从条件到 for 表达式全家桶

2.1 条件与语法糖

if (check) happy else sad

Scala 的if/else表达式而非语句——它总有返回值。当省略else分支时,编译器自动补全()(即Unit实例):

if (check) happy same as if (check) happy else ()

这一「语法糖」意味着:if (check) happy的类型是Anyhappy的类型与Unit的公共父类型),如果你期待它返回某个具体类型,必须显式写出else分支。

2.2 while 与 do-while 循环

while (x < 5) { println(x); x += 1}
do { println(x); x += 1} while (x < 5)

两个循环与 Java 语义一致,但注意:循环整体表达式的返回值是Unit,因此它天然适合「副作用驱动」的场景;纯函数式代码通常优先使用递归或集合操作代替循环。

2.3 break 跳出循环

Scala 2 没有原生的break关键字,而是通过scala.util.control.Breaks包以「异常抛出/捕获」的方式模拟:

import scala.util.control.Breaks._ breakable { for (x <- xs) { if (Math.random < 0.1) break } }

breakable { ... }建立一个作用域,break在其中抛出受控异常中断循环。这带来一个已知代价:break依赖异常机制,性能敏感的热路径上不建议滥用;更函数式的替代方案是takeWhileexists或在for内使用守卫条件。

2.4 for 表达式:filter/map 等价转换

for (x <- xs if x%2 == 0) yield x*10 // 等价于 xs.filter(_%2 == 0).map(_*10)

for ... yield(for-comprehension)是 Scala 最强大的语法糖之一。带if守卫的生成器在编译期会被脱糖(desugar)为filter+map的组合——这一等价关系是理解 for 表达式行为的关键:凡是能实现map/flatMap/withFilter/foreach的类型(List、Option、Future、自定义集合等),都可以出现在 for 表达式中

2.5 for 表达式:解构绑定

for ((x,y) <- xs zip ys) yield x*y // 等价于 (xs zip ys) map { case (x,y) => x*y }

zip将两个集合配对为(x,y)元组序列;for 表达式左侧的(x,y)模式,逐元素解构元组。脱糖后的map { case (x,y) => x*y }必须使用case模式匹配写法(圆括号map((x,y) => ...)会把(x,y)误当作单一元组参数,见下文模式匹配章节)。

2.6 for 表达式:叉乘(flatMap 展平)

for (x <- xs; y <- ys) yield x*y // 等价于 xs flatMap {x => ys map {y => x*y}}

多个生成器(以分号或换行分隔)构成嵌套迭代,语义与两层嵌套循环一致。脱糖后外层是flatMap、内层是map——这正是「叉乘/笛卡尔积」的标准实现:对xs的每个x,把ys映射后展平拼接。

2.7 for 表达式:无 yield 的副作用形式

for (x <- xs; y <- ys) { println("%d/%d = %.1f".format(x, y, x/y.toFloat)) }

没有yield的 for 表达式等价于foreach,返回值是Unit,适合执行打印、写日志等副作用操作。示例中还展示了 Scala 的String.format风格插值"%d/%d = %.1f".format(x, y, x/y.toFloat),其中x/y.toFloat保证了除法结果为浮点数而不是整数截断。

2.8 for 表达式:Range 遍历的上界差异

for (i <- 1 to 5) { println(i) }

1 to 5生成包含 5 的 Range,打印1,2,3,4,5

for (i <- 1 until 5) { println(i) }

1 until 5生成排除 5 的 Range,打印1,2,3,4。二者与 2.6 节中1 to 5/1 until 6的等价关系一致——to含上界、until不含上界,在写循环边界时务必区分,避免差一(off-by-one)错误。

三、模式匹配:函数参数中的模式与稳定标识符陷阱

3.1 在函数参数中直接使用模式

// ✅ GOOD (xs zip ys) map { case (x,y) => x*y } // ❌ BAD (xs zip ys) map( (x,y) => x*y )

当需要对单个参数做模式解构时,必须写成{ case (x,y) => ... }(称为「模式匹配匿名函数」)。而map((x,y) => x*y)(x,y)会被编译器解读为「一个类型为Tuple2的参数」而非两个参数,导致类型错误——这是 Scala 初学者最常踩的坑之一。

3.2 模式变量遮蔽(Stable Identifier)陷阱

Scala 的模式匹配有一个反直觉的规则:以小写字母开头的模式名会被当作「新的模式变量」,即使它与外层已存在的val同名,也会遮蔽(shadow)后者:

val v42 = 42 Some(3) match { case Some(v42) => println("42") case _ => println("Not 42") }

❌ BAD: “v42” 被解释为可以匹配任何Int类型值的名称,打印输出”42”

此处Some(3)匹配Some(v42)时,v42被绑定为3(匹配任何 Int 都成功),分支必然命中并打印 "42",外层的val v42 = 42完全被忽略。

解决办法一:反引号引用稳定标识符

val v42 = 42 Some(3) match { case Some(`v42`) => println("42") case _ => println("Not 42") }

✅ GOOD: 有反引号的 “v42” 被解释为已经存在的val v42,所以输出的是 “Not 42”.

反引号`v42`告诉编译器:「这里引用的是已存在的稳定标识符(stable identifier),按值比较」,因此3 != 42,落入case _,输出 "Not 42"。

解决办法二:以大写字母开头的标识符

val UppercaseVal = 42 Some(3) match { case Some(UppercaseVal) => println("42") case _ => println("Not 42") }

✅ GOOD: UppercaseVal 被视作已经存在的 val,而不是一个新的模式变量,因为它是以大写字母开头的,所以UppercaseVal所包含的值(42)和检查的值(3)不匹配,输出”Not 42”

语言规范约定:以大写字母开头的标识符在模式中默认按值引用已有定义,而非引入新变量。因此case Some(UppercaseVal)等价于按42匹配,3不匹配,落入case _输出 "Not 42"。

实用建议:当你在match中想「引用外层常量做相等比较」时,要么给常量用大写开头的名字,要么用反引号包裹,二者任选其一,切勿依赖小写同名变量——它只会被静默地当作新绑定。

四、面向对象:构造器、继承、特质与类型系统

4.1 构造器参数:私有与公有

class C(x: R) same as class C(private val x: R) var c = new C(4)

类参数不带修饰符(仅x: R)时,它只是构造器参数,不生成任何字段——外部无法访问c.x,类内部也无法通过this.x访问。这与显式private val x等价(后者明确声明私有不可变字段)。

class C(val x: R) var c = new C(4) c.x

val修饰的类参数会自动生成公有只读字段(含 getterx),因此c.x可直接读取。若需要可变公有字段,用var x: R

4.2 构造函数就是类的主体

class C(var x: R) { assert(x > 0, "positive please") var y = x val readonly = 5 private var secret = 1 def this = this(42) }

Scala 没有独立的构造方法——类体本身就是主构造函数,类体中的语句在实例化时按顺序执行:

  • assert(x > 0, "positive please")在构造期校验参数,不满足立即抛出AssertionError
  • var y = x声明一个由参数初始化的成员字段;
  • val readonly = 5声明只读字段;
  • private var secret = 1声明外部不可见的私有可变状态;
  • def this = this(42)定义辅助构造函数(auxiliary constructor),必须以this(...)委托给主构造函数或其它辅助构造器开头。

4.3 匿名类、抽象类与继承

new{ ... } // 匿名类 abstract class D { ... } // 不可创建 class C extends D { ... }
  • new { ... }创建匿名类,可以立即实现若干方法或字段,常用于快速构造一次性对象。
  • abstract class D声明抽象类,不能直接实例化,只能被子类继承。
  • class C extends D { ... }定义继承子类。

4.4 继承与构造器参数传递

class D(var x: R) class C(x: R) extends D(x)

子类构造器必须把参数显式传给父类构造器:extends D(x)。这里class C(x: R)中的x仅是构造参数(不生成字段),它被原样转发给父类D以初始化父类的var x。文档注记为「愿望清单: 默认自动传参」——即希望 Scala 能省去手动转发,但当前语言设计仍要求显式传递。

4.5 单例对象与特质(Trait)

object O extends D { ... }

object定义单例对象(Singleton),是 Scala 实现「无静态成员」语言哲学的方式——object的成员相当于 Java 中的静态成员,且object同样可以继承类、混入特质,表现得「和模块一样」。

trait T { ... } class C extends T { ... } class C extends D with T { ... }

trait(特质)是「带有实现的接口,没有构造参数」:它既能像 Java 接口一样声明抽象方法,也能提供默认实现,还支持字段。类可以extends单个特质,也可以extends类再with混入特质。

trait T1; trait T2 class C extends T1 with T2 class C extends D with T1 with T2

多个特质通过with依次混入;在已有父类的情况下,同样是extends 父类 with 特质1 with 特质2的链式语法。

4.6 方法覆盖必须显式声明

class C extends D { override def f = ...}

Scala 规定:覆盖父类或特质中的具体/抽象方法时,必须显式写override关键字,否则编译报错。这一强制检查能提前发现「本想新增方法却意外重写了父类方法」之类的错误,比 Java 的@Override可选注解更严格。

4.7 创建对象与类字面量

new java.io.File("f") new List[Int] // ✅ GOOD List(1,2,3) // ❌ BAD
  • new java.io.File("f")常规实例化;
  • new List[Int]直接构造不可变列表是合法的(得到一个空列表);
  • List(1,2,3)实际调用的是伴生对象的apply工厂方法,返回List[Int]——注释标注为 ❌ BAD,是提醒读者:List类型本身不是构造器,直接new List(1,2,3)并不成立,应使用伴生对象的apply语法。
classOf[String]

classOf[String]获取类字面量(Class[String]),对应 Java 的String.class,常用于反射与泛型运行时信息获取。

4.8 类型检查、强制转换与归属

x.isInstanceOf[String] // 类型检查 (运行时) x.asInstanceOf[String] // 类型强制转换 (运行时) x: String // 归属 (编译时)
  • x.isInstanceOf[String]运行时类型检查,等价于 Java 的instanceof,返回Boolean
  • x.asInstanceOf[String]运行时强制转换,等价于 Java 的(String) x;若运行时类型不符会抛出ClassCastException
  • x: String编译期类型归属(type ascription),仅做编译期检查,不产生运行时开销;它只是把表达式的静态类型「标注/收窄」为String

工程建议:多数场景应优先用模式匹配(case s: String => ...)替代isInstanceOf+asInstanceOf的组合,前者既做检查又完成安全解包,且不会引入ClassCastException风险。

五、在 Quick Reference 仓库中的定位与使用方式

本文内容源自本仓库 docs/scala.md,它在仓库首页 README.md 的「编程」分类中以 Scala 主题卡片形式收录(卡片图标为 assets/scala.svg)。该速查表面向中文开发者,按「入门 → 控制结构 → 模式匹配 → 面向对象」四段式组织,每一段都用「✅ GOOD / ❌ BAD」的对比方式标注了惯用法与反模式,特别适合:

  • Java 开发者快速迁移:重点关注 1.1 节的过程/函数之分、1.5 节的柯里化、4.4 节的构造器传参;
  • 函数式初学者:重点研读 1.3~1.6 节的匿名函数与 2.4~2.8 节的 for 表达式等价转换;
  • 面试与代码评审前突击:3.2 节的模式变量遮蔽、4.6 节的override强制声明是高频考点。

如需在本地查阅完整速查表合集,可克隆本仓库后按 README.md 首页的分类索引逐篇浏览;该仓库由 docs 目录下数百份 Markdown 速查文档组成,Scala 是其中「编程」板块的一员。

结语

Scala 是一门「吸收 Java 生态、面向函数式与并发」的语言,其语法的许多陷阱恰恰来自它「表达式化」与「语法糖丰富」的设计取向:=缺失导致的过程退化、占位符_的多种语义、for 表达式的脱糖规则、模式变量对小写标识符的遮蔽……本文以 docs/scala.md 为骨架逐条拆解并补充了底层原理与脱糖细节。对照这些 ✅/❌ 对照示例练习,即可在实战中快速写出符合 Scala 惯用法的健壮代码。

【免费下载链接】reference面向开发者的技术速查清单(Cheat Sheets)集合,整理常见技术、工具与开发流程,帮助快速查阅关键信息,提高开发效率。项目地址: https://gitcode.com/GitHub_Trending/referen/reference

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 3:07:21

dshvm:像 nvm 管 Node 一样管理 dsh 版本,破解破坏性更新难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 3:06:15

Skills协议:可验证、可复用的能力建模与评分体系

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 3:05:49

Git入门指南:从安装配置到日常命令的完整实战手册

我不姓奶奶&#xff0c;但在这个行业里待得久了&#xff0c;办公室的小孩儿都这么喊我。这些年我带过的新人少说也有几十个&#xff0c;大家刚接触写代码时&#xff0c;绕不开的就是同一个坎&#xff1a;Git。Git这个词听起来很神&#xff0c;其实就是一套管代码的工具。你写的…

作者头像 李华
网站建设 2026/9/15 3:05:41

Codex VSCode插件安装配置与DeepSeek、GPT双模型实战指南

最近 Codex 这个开源编程智能体在开发者圈子里热度很高&#xff0c;OpenAI 把它从命令行一路做到了 VSCode 插件&#xff0c;装好之后&#xff0c;AI 可以直接在你编辑器里读代码、改代码、跑测试、提 PR&#xff0c;体验和以前那种网页聊天完全不一样。更关键的是&#xff0c;…

作者头像 李华
网站建设 2026/9/15 3:05:10

电力时序数据平台:Hadoop+Spark+SpringBoot工业级实践

简介&#xff1a;本资源是一套高分毕业设计级的电力生产数据分析系统&#xff0c;面向计算机、人工智能、自动化等专业的在校学生、教师及初级大数据开发者&#xff0c;解决电力行业数据采集、存储、分析与可视化的一站式实践需求。项目基于Hadoop生态构建&#xff0c;整合HDFS…

作者头像 李华
网站建设 2026/9/15 3:05:08

多小区NOMA下行功率分配:从SIC序列到MATLAB实现

简介&#xff1a;围绕多小区下行链路NOMA系统的最优功率分配问题&#xff0c;这套MATLAB源代码给出完整仿真实现&#xff0c;适合通信工程、电子信息与数学等专业学生完成课程设计、期末大作业或毕业设计。代码以加权最小均方误差迭代算法为主线&#xff0c;包含信道生成、串行…

作者头像 李华