Python 列表推导式深度解析
一、什么是列表推导式?列表推导式(List Comprehension)是 Python 中一种简洁、优雅的语法特性,用于快速创建列表。它允许我们在一行代码中完成对序列的迭代、过滤和转换操作,相比传统的 for 循环,代码更加简洁易读。 二、基本语法列表推导式的基本语法如下: 1[表达式 for 变量 in 可迭代对象 if 条件] 表达式:对每个元素执行的操作,结果将作为新列表的元素 变量:从可迭代对象中取出的每个元素 可迭代对象:可以是列表、元组、字符串、range 等 条件(可选):过滤条件,只有满足条件的元素才会被处理 三、基础用法3.1 简单列表生成123456789101112# 生成 0-9 的平方列表squares = [x ** 2 for x in range(10)]print(squares) # 输出: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]# 生成 1-10 的偶数列表evens = [x for x in range(1, 11) if x % 2 == 0]print(evens) # 输出: [2, 4...
树:BST、平衡树(AVL/红黑简介)与 B 树
你有没有想过一个尴尬的局面:有序数组查找快(二分 O(log n)),可一旦插入新元素,得把后面一半整体后挪,O(n);链表插入是 O(1),可查找又退化成 O(n)。有没有一种结构,能既要查找快、又要插入/删除也快? 答案就是本篇的主角——树,更准确地说是**二叉搜索树(BST)**以及它的「打补丁」版本:平衡树(AVL / 红黑树)和多路平衡树(B 树)。它们把「有序」从「数组的连续内存」里解放出来,变成「节点之间的偏序关系」,于是插入、删除不再需要搬移整段数据。我们先从最朴素的 BST 讲起,再看它哪里会翻车,以及三个补丁分别怎么修。 一、二叉搜索树(BST):定义与不变式BST = 一棵二叉树,且满足「左小右大」的递归不变式: 对树上任意一个节点 x:其左子树中所有 key 都 < x.key,其右子树中所有 key 都 > x.key。 注意是「严格」的偏序,通常不允许重复 key(要支持重复就改成「≤ 放左 / ≥ 放右」,本篇按不允许重复讲,逻辑最干净)。这条不变式是 BST 一切能力的根基——因为它意味着中...
CMake 集成 Lcov 生成代码覆盖率报告
一、工具链安装(环境准备阶段)代码覆盖率分析依赖 lcov(数据处理)、gcov(数据生成)、genhtml(报告可视化)三款核心工具,需根据操作系统选择对应安装方式。 1.1 Debian/Ubuntu 系统通过 apt 包管理器一键安装,命令如下: 1sudo apt update && sudo apt install -y lcov gcov genhtml lcov:负责收集、过滤、合并覆盖率原始数据 gcov:编译器内置组件(GCC 默认自带,Clang 需确保版本 ≥9.0) genhtml:将 lcov 数据转换为带代码标注的 HTML 报告 1.2 工具版本验证安装完成后需确认工具可用性与版本兼容性,避免因版本过低导致功能异常: 1234# 验证 lcov 版本(需 ≥1.16,支持现代 CMake 路径映射)lcov --version# 验证编译器覆盖率组件(GCC ≥7.0,Clang ≥9.0)gcov --version 二、CMake 配置(编译配置阶段)在项目根目录的 CMakeLists.txt 中添加覆盖...
栈与队列:LIFO 与 FIFO 的两种世界观
你有没有想过,为什么编辑器里按 Ctrl+Z 能一步步撤销,而打印机却老老实实按提交顺序一张张出?这两件事看似无关,底层却是同一对"基础设施"的两种相反用法:一个只许从同一端进出,一个必须从两端分工。 这就是本篇的主角——栈(Stack) 和 队列(Queue)。它们都属于上篇讲过的 ADT:只规定"能做什么",不规定"怎么存"。但正是这两种最朴素的秩序观,撑起了从函数调用、表达式求值到任务调度、消息队列的半壁江山。我们先把"世界观"立住,再动手实现,最后看两个几乎人人都踩过的坑。 一、栈(Stack):后进先出(LIFO)的世界观栈 = 只允许在同一端(栈顶)进行插入和删除的 ADT,语义是 LIFO(Last-In-First-Out,后进先出)。 想象一摞盘子:你只能在最上面放新盘子(push),也只从最上面拿走(pop),永远碰不到底下那个。最后放上去的,必然第一个被取走——这就是"后进先出"。 栈暴露给客户的契约只有四个操作: 操作 含义 异常 p...
Python 解包操作:* 和 ** 深度解析
一、 什么是解包操作?解包(Unpacking)是 Python 中一种强大的语法特性,它允许我们将容器类型(如列表、元组、字典等)中的元素“解压”出来,分别赋值给多个变量。Python 提供了两种主要的解包操作符: *:用于序列解包(列表、元组、字符串等可迭代对象) **:用于字典解包(将键值对解包为关键字参数) 二、 基础解包:无需操作符的简单情况在学习 * 和 ** 之前,我们先了解一下最基本的解包操作: 123456789101112131415# 基本解包 - 左右两边元素数量必须匹配name, age, city = ["Alice", 30, "New York"]print(name) # 输出: Aliceprint(age) # 输出: 30print(city) # 输出: New York# 元组解包同样适用coordinates = (10.5, 20.7)x, y = coordinatesprint(x, y) # 输出: 10.5 20.7# 字符串解包word = "abc"...
Lcov的基础使用
导言在软件开发过程中,代码覆盖率是衡量测试质量的关键指标之一。它能够帮助开发和测试团队识别未被测试覆盖的代码区域,从而提升软件质量和稳定性。Lcov(Linux Test Project Coverage Tool)作为一款强大的代码覆盖率分析工具,基于 GCC 的覆盖测试功能,能够生成直观的 HTML 报告,广泛应用于 Linux 环境下的软件开发流程。本文将从基础概念入手,带您逐步掌握 Lcov 的安装、配置、使用及数据分析,轻松入门代码覆盖率分析。 一、Lcov 基础概念:你需要了解的核心术语在使用 Lcov 之前,首先需要理解代码覆盖率的基本概念,这将帮助你更好地解读 Lcov 生成的报告。 术语 定义 作用 代码覆盖率(Code Coverage) 衡量测试用例执行时覆盖代码比例的指标,反映测试的充分性 评估测试质量,识别未测试代码 行覆盖(Line Coverage) 被测试执行过的代码行数占总代码行数的比例 最基础的覆盖率指标,直观反映代码执行情况 分支覆盖(Branch Coverage) 被测试执行过的代码分支(如 if/else、...
链表(单/双)与哨兵
上篇动态数组最大的软肋是:在中间插入或删除一个元素,要搬移其后所有的元素,代价 O(n)。链表(linked list)号称能绕开这个软肋——它不靠"连续内存 + 下标"组织数据,而是让每个元素自己记住"下一个是谁"。 但"链表删除是 O(1)"这句话,九成初学者都记漏了前提。这篇就把单链表、双链表、以及那个能把一堆边界特判消灭干净的**哨兵节点(sentinel)**讲透,顺便留三道面试开胃题。 一、单链表:每个节点只认识"下一个"单链表由一串 节点(Node) 串成。每个节点干两件事:存自己的数据 val,再存一个指向下一个节点的引用 next。最后一个节点的 next 是 null,表示"到头了"。一个独立的 head 引用指向第一个节点,靠它才能摸到整条链。 1 val next 2 val next 3 val next null head 本质一句话:单链表用&...
数组与动态数组(ArrayList)及均摊分析
你每天写 ArrayList、vector、list.append(),从没操心过"容量"——它好像永远装得下。但每次"满了",它其实偷偷把整片数据搬了一次家:分配更大的内存、把旧元素逐个复制过去、再扔掉旧的。这一篇把这件"看不见的搬家"讲透,并回答一个反直觉的问题:为什么扩容是 O(n),尾部追加却是 O(1)? 答案叫均摊分析(amortized analysis)。 一、静态数组:快,但僵数组是最原始的数据结构:N 个同类型元素连续摆在内存里,靠下标直接寻址。 优点:随机访问 a[i] 是 O(1)——CPU 算一下 基地址 + i × 元素大小 就能取到,不依赖数组长度。 死穴:容量在创建时就钉死了。想在第 0 位插一个?后面所有元素都得往后挪,O(n)。更糟的是,满了就彻底塞不下了。 数组这层"裸"结构,正是上一篇说的"只承诺怎么存、不约束怎么用"。动态数组要解决的,就是给它套一层自动扩容的抽象。 二、动态数组:在数组上套一层"自动扩容"动态数...
Python PIL库使用指南
一、什么是PIL?PIL(Python Imaging Library)是Python中最常用的图像处理库,它提供了丰富的图像处理功能,如打开、保存、调整大小、裁剪、旋转、滤镜等。PIL已经被Pillow库所取代,Pillow是PIL的一个分支,提供了更多的功能和更好的支持。 二、安装Pillow1pip install Pillow 三、基本操作1. 打开和显示图像123456789101112from PIL import Image# 打开图像img = Image.open('image.jpg')# 显示图像img.show()# 查看图像信息print(f"图像大小:{img.size}")print(f"图像模式:{img.mode}")print(f"图像格式:{img.format}") 2. 保存图像12345678from PIL import Image# 打开图像img = Image.open('im...
CMake 案例实战:构建多文件计算项目
导言在掌握 CMake 基础用法后,本文将通过一个完整的多文件计算项目案例,深入讲解 CMake 在实际开发中的应用。该案例包含加减乘除四个运算模块,通过 CMake 实现自动化构建,同时覆盖源文件搜索、头文件路径配置、变量使用等核心技巧,帮助你将 CMake 知识落地到实际项目中。 一、项目整体概览1.1 项目功能该项目实现了整数的加减乘除基本运算,通过main.cpp中的test()函数调用各运算模块,最终在控制台输出计算结果。项目结构清晰,将不同运算逻辑拆分到独立的源文件和头文件中,符合模块化开发思想。 1.2 完整文件结构1234567891011calc_project/├── add.cpp # 加法运算实现├── add.h # 加法运算声明├── CMakeLists.txt # CMake配置文件├── divi.cpp # 除法运算实现├── divi.h # 除法运算声明├── main.cpp # 主程序(测试入口)├── mult.cpp # 乘法运算实现├── mult...
CMake 初步使用
CMake 初步使用CMake 是一个跨平台的构建系统生成工具,它可以根据简单的配置文件(CMakeLists.txt)生成不同平台的构建文件(如 Makefile、Visual Studio 项目文件等)。对于 C/C++ 项目,掌握 CMake 的基本使用能极大简化跨平台开发流程。 一、CMake 基本概念 CMakeLists.txt:CMake 的配置文件,描述项目的构建规则 构建目录:存放生成的构建文件和编译产物的目录,通常建议与源代码分离 生成器:CMake 支持的不同构建系统(如 Unix Makefiles、Visual Studio、Xcode 等) 目标(Target):CMake 中要构建的实体(可执行文件、库等) 二、安装 CMake Windows:从 CMake 官网 下载安装包,勾选 "Add CMake to the system PATH" Linux:通过包管理器安装 sudo apt install cmake(Debian/Ubuntu)或 sudo yum install cmake(C...
数据结构导论:从数组到抽象数据类型(ADT)
很多人学完数组、链表、栈、队列,仍有一个挥之不去的困惑:这些"数据结构"到底有没有区别?为什么教科书总说 Stack 是 ADT 而不是数据结构? 如果你也含糊,这篇就是为你写的——我们不背定义,先把这层窗户纸捅破。 缘起很朴素:数组是最原始、最"诚实"的数据结构,它把 N 个同类型元素连续摆在内存里,你用下标直接取。但它太"裸"了——它既不阻止你越界访问,也不阻止你在第 0 位插一个元素(后面所有元素都得搬)。于是自然会想:能不能定义一种"只许从一端进出"的抽象,把数组这种杂乱用法管起来? 这正是抽象数据类型(ADT)的动机:从"怎么存"里提炼出"能做什么"。 一、先别急着写代码:什么是抽象数据类型(ADT)ADT = 一组数据值 + 一组在这些值上可做的操作(契约),它只规定"你能对我做什么",不规定内部怎么存。 最熟悉的例子其实天天在用:整数就是 ADT。你知道 +、-、* 怎么用,但从不在乎 CPU 里是用补码还是浮点表示...
Scheme 解释器、尾递归与声明式 SQL
前七篇都在 Python 里打转。最后一篇,CS61A 故意把你拽进另一种语言——Scheme(Lisp 方言),再让你亲手写一个"能运行 Scheme 的解释器"。为什么?因为跳出一种语言,你才真正看清"编程"这件事的边界:原来函数可以没有名字、原来递归可以不用占栈、原来"我要什么"和"我怎么做"可以彻底分开。这一篇是 CS61A 的高潮,也是把全课程串起来的那根线。 一、Scheme 基础:括号即一切是什么:Scheme 里一切都是 S-表达式(S-expression)——要么是一个原子(数字、符号),要么是括号包裹的组合 (操作符 操作数 ...)。所有语法都是这一种形式的变体。 1234(define (square x) (* x x)) ; 定义过程 square(square 5) ; => 25(define x 10)(if (> x 0) "正" "负") ...
Python CSV模块使用指南
一、什么是CSV?CSV(Comma-Separated Values)是一种简单的文件格式,用于存储表格数据,如电子表格或数据库。CSV文件中的每行代表表格中的一行,每行中的值用逗号(或其他分隔符)分隔。 二、Python的CSV模块Python标准库中的csv模块提供了处理CSV文件的功能,它可以帮助你读取和写入CSV文件,处理各种CSV格式的变体。 三、读取CSV文件1. 基本读取123456import csvwith open('data.csv', 'r', encoding='utf-8') as f: reader = csv.reader(f) for row in reader: print(row) 2. 读取为字典123456import csvwith open('data.csv', 'r', encoding='utf-8') as f: reader = csv.DictReader(f) ...
C++核心语法整理
C++核心语法整理C与C++类与对象C++输入输出流友元与运算符重载关联式容器继承多态模板移动语义与资源管理C与C++ C++程序介绍 g++编译器安装 sudo apt install g++ 源文件名称 .cc .cpp C++程序模板设置 /home/st/.vim/plugged/prepare-code/snippet hello world程序分析 #include C++标准库中头文件 没有.h cin 标准输入流 默认输入设备 从键盘接收数据 cout 标准输出流 默认输出设备 屏幕 int main(int argc , char *argv[]){} 返回值为int argc 命令行参数的个数 argv 具体的命令行参数 vim中启动鼠标 编辑.vimrc文件 子主题 命名空间 命名空间是什么, 有什么作用? C++中的一种避免名字冲突的机制 主要作用区分同名实体 实体 变量、常量、函数、结构体、类、对象...
SAX vs. DOM:流式处理与树状模型
一、核心区别:内存快照 vs. 事件流先明确最本质的差异:DOM 解析器会为整个XML文档创建一个内存快照,构建一棵完整的节点树;而 SAX 解析器则像一个事件流处理器,逐行扫描文档并触发事件。这一区别决定了它们在内存占用、处理速度和编程模型上的根本不同,是XML处理技术中“空间换时间”与“时间换空间”的经典对决。 二、分场景深度解析1. DOM:将整个文档“拍”进内存DOM(Document Object Model)的核心思想是一次性加载整个XML文档,在内存中构建一个与文档层级结构完全对应的对象树。这就像给一座建筑拍下一张高清全景照片,所有细节(房间、门窗、楼层关系)都一览无余。 工作原理:解析器从XML文件的根元素开始,递归地读取每个节点,并在内存中创建相应的对象(如 Document, Element, Attr, Text)。这些对象通过父子、兄弟关系相互连接,形成一个完整的对象树。 实现特点: 随机访问:由于整个树都在内存中,你可以随时、随意地访问树中的任何一个节点,向前或向后遍历都极其方便。 易于编程:其API非常直观,符合人们对树形结构的认知,上手简单,代...
效率、迭代器与生成器
回到篇三那个优雅的 fib:代码和数学定义一字不差,美不美?美。快不快?一点都不快。当 n 大到 40,它能卡到让你怀疑人生。为什么"正确的代码"会这么慢?又该怎么救?本篇先给你一把尺子(大 O)量效率,再用"迭代"和"记忆化"两把刀修掉 fib,最后引出 Python 里最被低估的利器——生成器,它让"无限序列"成为可能。 一、增长阶(大 O):规模翻倍时耗时怎么变是什么:衡量算法效率,不看"跑了 3 毫秒还是 5 毫秒"(那取决于机器和常数),而看输入规模 n 增长时,步数怎么随 n 变化。忽略常数倍,只看"增长趋势",就是大 O 记号。 阶 含义 例子 O(1) 常数,与 n 无关 列表按下标访问 O(n) 线性,随 n 正比增长 遍历一遍 O(n²) 平方,双层嵌套 冒泡排序 O(2ⁿ) 指数,n 每+1 量翻倍 朴素斐波那契 坑:大 O 丢掉了常数,所以"O(n) 一定比 O(n²) 快"只在 n 足...
func(int) & func(int x)
一、核心区别:参数名的「存在意义」先明确最本质差异:**func(int){}**省略参数名,**func(int x){}**指定参数名x。这一区别在函数「声明」和「定义」场景中影响截然不同,且仅在 C/C++ 等少数语言中合法(Python、Java 等需强制指定参数名)。 二、分场景深度解析1. 函数声明阶段:几乎无差异在头文件或函数原型声明中,两者作用完全一致 ——仅告知编译器「函数接收一个 int 类型参数」,参数名不影响函数签名。 示例: 123// 以下两种声明等效,编译器均识别为「接收int、返回void」的函数void func(int); // 省略参数名(常用)void func(int x); // 带参数名(可选,仅作注释提示) 正如中关村在线问答指出的:声明只需说明参数类型,参数名「没什么用」。编译器处理时,会忽略声明中的参数名,仅记录函数名和参数类型序列。 2. 函数定义阶段:可用性天差地别函数定义(实现)时,参数名的有无直接决定「能否在函数体内使用该参数」: func(int x){}:可正常操作参数 x是参数的...
STL标准模板库内容整理
STL标准模板库(重点):C++工具(类模板与函数模板) STL标准模板库 本质上就是数据结构和算法 C语言标准库未直接提供 C++标准库直接提供 定义:高效C程序库,含基本数据结构和算法,属C标准库,采用泛型编程 泛型编程:抽象数据类型,用泛型代替具体类型,编写通用代码 六大组件 容器(重要):存储数据(数据结构) 序列式容器:vector、list、deque等 关联式容器:set、map等 无序关联式容器:unordered_set、unordered_map等 迭代器:访问容器元素,泛型指针(例:vector::iterator) 算法:操作容器元素的普通函数(例:std::sort) 适配器:适配作用 容器适配器:stack、queue、priority_queue 迭代器适配器 函数适配器:bind、bind1st、bind2nd、function等 函数对象:实现定制化操作 空间配置器:管理内存(使用、原理、源码) 六大组件 容器 作用:存放数据 1、序列式容器 模型理解 array 静态数组,大小固定的数...
面向对象编程:类、继承与方法分派
前面我们一直把"数据"和"操作数据的函数"分开写:先定义 make_rat,再写 add_rat。但真实世界里,一个"学生"既有一堆属性(姓名、成绩),又有一堆行为(选课、算 GPA)。把它们拆在两处,代码会越来越散、越来越难找。面向对象(OOP) 的解法很直白:把"数据"和"操作它的方法"打包进同一个类(class)。本篇从 self 这个最小齿轮讲起,一直讲到 Python 怎么决定"调的是哪个方法"(MRO)。 一、类与实例:蓝图与成品是什么: 类(class) 是对象的"蓝图",描述一类事物长什么样、能干什么; 实例(instance) 是按蓝图造出的具体对象; self 是约定俗成的第一个参数,代表"当前这个实例自己"。 12345678class Dog: def __init__(self, name): # 构造器,创建时自动调用 self.name = name ...
拒绝死记硬背!用 C++ 的底层逻辑,彻底搞懂 Python 的“魔术方法”
一、引言:打破“魔法”的迷信初学者看到 __init__、__str__ 这种双下划线方法就头大,只能死记硬背。但对于C++程序员来说,这些其实就是编译器在特定时刻自动调用的“钩子函数”。 二、核心类比 Python 的 obj + obj 对应 C++ 的 operator+ 重载 Python 的 str(obj) 对应 C++ 的 operator std::string() 或 toString() 虚函数 Python 的 cls 对应 C++ 的 static 类作用域 三、变身术:类型转换协议 (str vs int)1. __str__ (人类视图) C++ 类比:相当于 C++ 中重载 std::ostream& operator<<(std::ostream&, const T&) 触发时机:当你执行 print(obj) 或 str(obj) 时 用途:返回一个人类可读的字符串 2. __repr__ (机器视图) C++ 类比:相当于调试器中显示对象的逻辑 触发时机:当你在交互式终端中直接输入对象名并回车时 用途:返...
可变性、容器、树与链表
前面几篇,我刻意没让你碰"改掉一个已有的值"。因为**可变性(mutability)**是 CS61A 乃至整个编程里"便利"和"灾难"同一个源头。它能让代码更短更自然,也能让 bug 在最意想不到的地方炸——而且炸得毫无痕迹。本篇先把"可变 vs 不可变"这条生死线划清,再看别名、容器,最后动手实现 CS61A 两尊经典递归结构:Link 与 Tree。 一、可变 vs 不可变:一条分界生死线是什么: 不可变(immutable):创建后内容不能改。int、float、str、tuple、frozenset 都在此列。你做的任何"修改"其实是生成新对象。 可变(mutable):创建后内部可改。list、dict、set、以及你自己写的类实例都在此列。改的是"同一个对象"。 12345s = "abc"# s[0] = "x" # 报错:str 不可变lst = [1, 2, 3]lst[0] = ...
C++ 实现高效单词转换工具
一、需求与设计分析1.1 核心需求根据 C++ Primer 11.3.6 练习要求,工具需满足以下功能: 规则加载:从map.txt读取替换规则(每行格式:待替换单词 替换后的短语) 文本处理:读取file.txt中的待转换文本,将匹配规则的单词替换为对应短语 结果输出:将替换后的文本写入output.txt 灵活性:支持通过命令行参数自定义规则文件、输入文件和输出文件路径 鲁棒性:处理文件打开失败、格式错误等异常情况 1.2 示例输入输出 规则文件(map.txt):定义替换映射 12345678brb be right backk okay?y whyr areu youpic picturethk thanks!l8r later 待转换文本(file.txt):包含缩写词的原始文本 123where r uy dont u send me a pick thk l8r 预期输出(output.txt):替换后的标准文本 123where are youwhy dont you send me a pictureokay? thanks! later ...
数据抽象与序列
前几篇我们玩的"值"都是数字、字符串这种原子。可真实程序里,数据从来不是孤立的——一个学生有姓名、年龄、成绩;一个有理数有分子、分母。当数据变复杂,"怎么存"和"怎么用"就会纠缠在一起,改一处崩一片。CS61A 给出的解药叫数据抽象(data abstraction):用一层"接口"把两者隔开。本篇就用"有理数"这个小例子,把这个影响你一辈子的思想钉死。 一、数据抽象:构造函数 + 选择器是什么:CS61A 对"数据"有一个极简也极深刻的定义——数据 = 构造函数(constructor) + 选择器(selector)。 构造函数:把零散的部件"打包"成一个整体(如 make_rat(n, d)); 选择器:从整体里"取出"某个部件(如 numer(r) 取分子、denom(r) 取分母)。 只要这两者行为一致,内部到底用元组、字典还是两个独立变量存,根本不重要。这层隔离叫抽象壁垒(abstractio...
Python装饰器本质深度解析
一、装饰器的本质装饰器本质上就是高阶函数加语法糖。你想啊,装饰器做的事情就是:接收一个函数,包一层,返回一个新的函数。这不就是典型的高阶函数吗? @decorator 等价于 func = decorator(func),就是把原函数传给装饰器,装饰器决定什么时候调用它、传什么参数进去,然后再返回一个升级后的函数。 二、装饰器的基本结构让我们来看一个最简单的装饰器: 1234567891011121314def simple_decorator(func): def wrapper(*args, **kwargs): print("Before function call") result = func(*args, **kwargs) print("After function call") return result return wrapper@simple_decoratordef say_hello(name): print(f"Hello, ...

