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

JavaScript编译原理

在编程语言的世界中,编译器(如 GCC、TypeScript)和转译器(如 Babel)扮演着至关重要的角色,它们负责将人类可读的代码转换为机器或其他语言可执行的指令。这一过程通常分为四个关键阶段:

1. 词法分析:将源代码拆解成基本单元(Token);

2. 语法分析:根据语法规则构建抽象语法树(AST);

3. 代码转换:对 AST 进行优化或转换(如ES6 =>ES5);

4. 代码生成:将 AST 还原为目标代码(如JavaScript、机器码);

无论是传统编译器(如 C 语言编译器),还是现代前端工具链(如 Babel、Webpack),都遵循这一核心流程。理解这些步骤,不仅能帮助开发者更高效地调试代码,还能为自定义语言或工具开发奠定基础。

接下来,我们将详细解析每个阶段的技术原理和实现方法。

1. 编译过程

1.1. 词法分析

将源代码转换成单词流,称为"词法单元",每个词法单元包含一个标识符和一个属性值,比如变量名、数字、操作符等等。词法分析生成Token的方式主要有两种:

1.1.1. 正则

很多同学可能遇到字符串匹配的操作,自然就想到正则匹配。在普通场景下,正则用来做字符串匹配没什么问题,但如果是在编译器场景下,就很不合适,因为正则模式首先需要写大量的正则表达式,正则之间还有冲突需要处理,另外正则不容易维护,并且性能不高。

正则只适合一些简单的模板语法,真正复杂的语言并不合适。并且有的语言并不一定自带正则引擎。

1.1.2. 状态机

自动机可以很好的生成 token。

有穷状态自动机(finite state machine):在有限个输入的情况下,在这些状态中转移并期望最终达到终止状态。

有穷状态自动机根据确定性可以分为:

1. 确定有穷状态自动机(DFA - Deterministic finite automaton)

在输入一个状态时,只得到一个固定的状态,DFA 可以认为是一种特殊的 NFA。

2. 非确定有穷自动机(NFA - Non-deterministic finite automaton)

当输入一个字符或者条件得到一个状态机的集合,JavaScript 正则采用的是 NFA 引擎。

1.2. 语法分析

将词法单元流转换成抽象语法树(Abstract Syntax Tree,简称AST),也就是标记所构成的数据结构,表示源代码的结构和规则。

我们日常所说的编译原理就是将一种语言转换为另一种语言。编译原理被称为形式语言,它是一类无需知道太多语言背景、无歧义的语言。而自然语言通常难以处理,主要是因为难以识别语言中哪些是名词哪些是动词哪些是形容词。例如:“进口汽车”这句话,“进口”到底是动词还是形容词?所以我们要解析一门语言,前提是这门语言有严格的语法规定。

1956年,乔姆斯基将文法按照规范的严格性分为0型、1型、2型和3型共4种文法,从0到3文法规则是逐渐增加严的。一般的计算机语言是2型,因为0和1型文法定义宽松,将大大增加解析难度、降低解析效率,而3型文法限制又多,不利于语言设计灵活性。2型文法也叫做上下文无关文法(CFG)。

语法分析的目的就是通过词法分析器 拿到的 token 流 + 结合文法规则,通过一定算法得到一颗抽象语法树(AST)。抽象语法树是非常重要的概念,尤其在前端领域应用很广。典型应用如babel插件,它的原理就是:es6代码 → Babylon.parse → AST → babel-traverse → 新的AST → es5代码。

从生成AST效率和实现难度上,前人总结主要有2种解析算法:

1. 自顶向下的分析方法和自底向上的分析方法。自底向上算法分析文法范围广,但实现难度大;

2. 自顶向下算法实现相对简单,并且能够解析文法的范围也不错,所以一般的 compiler 都是采用深度优先索引的方式;

1.3. 代码转换

在AST上执行类型检查、作用域检查等操作,以确保代码的正确性和安全性。

在得到AST后,我们一般会先将AST转为另一种AST,目的是生成更符合预期的AST,这一步称为代码转换。

代码转换的优势主要是产生工程上的意义:

1. 易移植:与机器无关,所以它作为中间语言可以为生成多种不同型号的目标机器码服务;

2. 机器无关优化:对中间码进行机器无关优化,利于提高代码质量;

3. 层次清晰:将AST映射成中间代码表示,再映射成目标代码的工作分层进行,使编译算法更加清晰;

对于一个Compiler而言,在转换阶段通常有两种形式:

1. 同语言的AST转换;

2. AST 转换为新语言的 AST;

这里有一种通用的做法是,对我们之前的AST从上至下的解析,然后会有个映射表,把对应的类型做相应的转换。

1.4. 代码生成

基于AST生成目标代码,包括优化代码结构、生成代码文本、进行代码压缩等等。

在实际的代码处理过程中,可能会递归的分析我们最终生成的AST,然后对于每种 type 都有个对应的函数处理,实现思路用到的设计模式就是策略模式。

2. 完整链路

input => tokenizer => tokens;  // 词法分析
tokens => parser => ast;       // 语法分析,生成AST
ast => transformer => newAst;  // 中间层代码转换
newAst => generator => output; // 生成目标代码

3. 公式编译器实战

通过开发公式编译器与公式执行器,来进一步学习编译器的内容。

假设我们的语法诸如:

ADD(1, MINUS(3, 2))

Subtract(Add(3, Multiply(4, 2)), Divide(6, 2), 1)

3.1. 定义Token及分词器

定义Token如下:

const FN_NAME_TOKEN = /[a-zA-Z]/;
const NUMBER_TOKEN = /\d/;
const PAREN_TOKEN = /\(/;
const ATI_PAREN_TOKEN = /\)/;
const COMMA_TOKEN = /\,/;

实现分词器 Tokenizer:

function tokenizer(expression) {const tokens = [];let current = 0;while (current < expression.length) {let char = expression[current];// 先匹配数字if (NUMBER_TOKEN.test(char)) {let number = "";// 一直往后找,直到不是数字为止while (NUMBER_TOKEN.test(char)) {number += char;char = expression[++current];}// 将匹配到的 token 加入到 tokens 中tokens.push({type: "number",value: parseInt(number),});continue;}// 匹配函数名if (FN_NAME_TOKEN.test(char)) {let fnName = "";while (FN_NAME_TOKEN.test(char)) {fnName += char;char = expression[++current];}tokens.push({type: "function",value: fnName,});continue;}// 匹配括号和逗号if (PAREN_TOKEN.test(char) ||ATI_PAREN_TOKEN.test(char) ||COMMA_TOKEN.test(char)) {tokens.push({type: char,value: char,});current++;continue;}// 处理空格if (char === " ") {current++;continue;}throw new TypeError("I dont know what this character is: " + char);}return tokens;
}

3.2. 实现Parser解析器

parser 根据生成的 token 流进行转换,转化为我们预先预定好的 ast,生成的 ast 可供后续代码转换生成或者公式内容执行用。

function parser(tokens) {let current = 0;// 递归解析function walk() {let token = tokens[current];// 处理数字if (token.type === "number") {current++;return {type: "NumberLiteral",value: token.value,};}// 处理函数if (token.type === "function") {current++;let node = {type: "CallExpression",name: token.value,params: [],};token = tokens[++current];// 一直循环往复的收集参数,知道遇到右括号位置while (token.type !== ")") {node.params.push(walk());token = tokens[current];// 注意一点,如果遇到了参数中间的逗号,也需要跳过if (token.type === ",") {current++;}}current++; // 跳过右括号return node;}throw new TypeError(token.type);}let ast = {type: "Program",body: [],};while (current < tokens.length) {ast.body.push(walk());}return ast;
}

用以上解析器解析如下公式:

ADD(1, MINUS(3, MULTIPLY(4, 2)))

执行结果示意如下:

3.3. 定义执行器

根据ast,编写遍历节点需要执行的运算。

function interpret(ast) {const operators = {Add: (a, b) => a + b,Subtract: (a, b, c) => a - b - c,Multiply: (a, b) => a * b,Divide: (a, b) => a / b,};function traverseNode(node) {switch (node.type) {case "NumberLiteral":return node.value;case "CallExpression":const args = node.params.map(traverseNode);const operator = operators[node.name];if (!operator) {throw new TypeError("Unknown function: " + node.name);}return operator(...args);default:throw new TypeError(node.type);}}return traverseNode(ast.body[0]);
}

3.4. 解析输入的公式

const expression = "Subtract(Add(3, Multiply(4, 2)), Divide(6, 2), 1)";
const tokens = tokenize(expression);
const ast = parse(tokens);
const result = interpret(ast);
console.log(result);

其实可以优化的点有很多,主要包括:

1. 将运算功能设计为插件,通过插件注册方式拓展运算功能;

2. 支持预设变量,在公式进行计算时,把预设变量对应值一起进行计算;

3.5. 最终完整代码

// 定义 token 类型
const FN_NAME_TOKEN = /[a-zA-Z]/;
const NUMBER_TOKEN = /\d/;
const PAREN_TOKEN = /\(/;
const ATI_PAREN_TOKEN = /\)/;
const COMMA_TOKEN = /\,/;// 定义 tokenizer 函数,将表达式转换为 token 数组
function tokenizer(expression) {const tokens = [];let current = 0;while (current < expression.length) {let char = expression[current];// 先匹配数字if (NUMBER_TOKEN.test(char)) {let number = "";// 一直往后找,直到不是数字为止while (NUMBER_TOKEN.test(char)) {number += char;char = expression[++current];}// 将匹配到的 token 加入到 tokens 中tokens.push({type: "number",value: parseInt(number),});continue;}// 匹配函数名if (FN_NAME_TOKEN.test(char)) {let fnName = "";while (FN_NAME_TOKEN.test(char)) {fnName += char;char = expression[++current];}tokens.push({type: "function",value: fnName,});continue;}// 匹配括号和逗号if (PAREN_TOKEN.test(char) ||ATI_PAREN_TOKEN.test(char) ||COMMA_TOKEN.test(char)) {tokens.push({type: char,value: char,});current++;continue;}// 处理空格if (char === " ") {current++;continue;}throw new TypeError("I dont know what this character is: " + char);}return tokens;
}// 定义 parser 函数,将 token 数组转换为 AST
function parser(tokens) {let current = 0;// 递归解析function walk() {let token = tokens[current];// 处理数字if (token.type === "number") {current++;return {type: "NumberLiteral",value: token.value,};}// 处理函数if (token.type === "function") {current++;let node = {type: "CallExpression",name: token.value,params: [],};token = tokens[++current];// 一直循环往复的收集参数,知道遇到右括号位置while (token.type !== ")") {node.params.push(walk());token = tokens[current];// 注意一点,如果遇到了参数中间的逗号,也需要跳过if (token.type === ",") {current++;}}current++; // 跳过右括号return node;}throw new TypeError(token.type);}let ast = {type: "Program",body: [],};while (current < tokens.length) {ast.body.push(walk());}return ast;
}// 定义 transformer 函数,将 AST 转换为新的 AST
function interpret(ast) {const operators = {Add: (a, b) => a + b,Subtract: (a, b, c) => a - b - c,Multiply: (a, b) => a * b,Divide: (a, b) => a / b,};function traverseNode(node) {switch (node.type) {case "NumberLiteral":return node.value;case "CallExpression":const args = node.params.map(traverseNode);const operator = operators[node.name];if (!operator) {throw new TypeError("Unknown function: " + node.name);}return operator(...args);default:throw new TypeError(node.type);}}return traverseNode(ast.body[0]);
}// 解析运算表达式
const expression = "Subtract(Add(3, Multiply(4, 2)), Divide(6, 2), 1)";
const tokens = tokenize(expression);
const ast = parse(tokens);
const result = interpret(ast);console.log(result);

4. 补充资料

ast 查看器:AST explorer

强大的 parser 生成器:ANTLR

官方 babel ast 结构:babel/packages/babel-parser/ast/spec.md at main · babel/babel · GitHub

v8 优化编译器:https://v8.dev/blog/maglev

Chromium V8:https://chromium.googlesource.com/v8/v8.git

the-super-tiny-compiler:GitHub - jamiebuilds/the-super-tiny-compiler: :snowman: Possibly the smallest compiler ever

Babel 可选链语法插件:babel/packages/babel-plugin-transform-optional-chaining at main · babel/babel · GitHub opt?.name

相关文章:

JavaScript编译原理

在编程语言的世界中&#xff0c;编译器&#xff08;如 GCC、TypeScript&#xff09;和转译器&#xff08;如 Babel&#xff09;扮演着至关重要的角色&#xff0c;它们负责将人类可读的代码转换为机器或其他语言可执行的指令。这一过程通常分为四个关键阶段&#xff1a; 1. 词法…...

Nature图形复现—两种快速绘制热图的方法

相信大家在科研过程中&#xff0c;会遇到热图&#xff0c;有时候会觉得热图理解起来比较困难&#xff0c;或者觉得绘制热图也比较困难。本期教程我们来深入了解热图、绘制热图。 热图是一种通过颜色深浅或色阶变化来直观展示数据分布、密度或数值大小的可视化工具。它在多个领域…...

MySQL数据库——视图

目录 一、视图是什么&#xff1f; 二、特点 三、创建视图 四.查询视图 五.更新视图 六.视图的作用 总结 一、视图是什么&#xff1f; 视图是从一个或多个表中导出的虚拟表&#xff0c;它本身不存储数据&#xff0c;而是基于 SQL 查询的结果集。 二、特点 1.虚拟性&#xff1…...

标贝科技:大模型领域数据标注的重要性与标注类型分享

当前&#xff0c;大模型作为人工智能领域的前沿技术&#xff0c;其强大的泛化能力和复杂任务处理能力&#xff0c;依赖于海量数据的训练。而数据标注&#xff0c;作为连接原始数据与大模型训练的关键桥梁&#xff0c;在这一过程中发挥着举足轻重的作用。​ 大模型的训练依赖海…...

MYSQL备份恢复知识:第一章:备份操作举例

1. 备份工具 MySQL数据库的备份方式有两大类&#xff1a;一是物理备份&#xff0c;它对数据文件和日志进行整体备份&#xff1b;二是逻辑备份&#xff0c;通过DUMP工具将数据导出。具体的方法有以下几种&#xff1a; • 物理备份&#xff0c;MEB工具&#xff0c;是商用版本推荐…...

VS Code怎么设置python SDK路径

一、通过命令面板快速切换&#xff08;推荐方法&#xff09; 打开命令面板 • 快捷键&#xff1a;CtrlShiftP&#xff08;Windows/Linux&#xff09;或 CmdShiftP&#xff08;macOS&#xff09; • 输入命令&#xff1a;Python: Select Interpreter&#xff0c;回车后显示所有检…...

[经验总结]删除gitlab仓库分支报错:错误:无法推送一些引用到“http:”

问题描述 删除gitlab远程仓库报错。 [wingasowingaso release]$ git push gitlab --delete release remote: GitLab: You can only delete protected branches using the web interface. To http://x.x.x.x/gitlab/test.git! [remote rejected] release (pre-receive hoo…...

虹科干货 | CAN XL安全实践:深度防御下的密钥协商优化

摘要 随着汽车以太网的兴起和车载通信系统数量的增加&#xff0c;网络整合成为控制复杂性和成本的关键。当前架构呈现明确分层&#xff1a;以太网&#xff08;100/1000Mbit/s&#xff09;支撑信息娱乐、ADAS等高带宽应用&#xff0c;而CAN/CAN FD&#xff08;0.5-5Mbit/s&#…...

Linux干货(一)

前言 从B站黑马程序员Linux课程摘选的学习干货&#xff0c;新手友好&#xff01;若有侵权&#xff0c;会第一时间处理。 1.Linux目录结构 1.Linux操作系统的目录结构 Windows系统可以拥有多个盘符&#xff0c;如C盘、D盘、E盘 Linux没有盘符这个概念&#xff0c;只有一个根…...

Scala和Go差异

Scala和Go&#xff08;又称Golang&#xff09;是两种现代编程语言&#xff0c;各自具有独特的特性和设计哲学。 尽管它们都可以用于构建高性能、可扩展的应用程序&#xff0c;但在许多方面存在显著差异。 Scala和Go的详细比较&#xff0c;涵盖它们的异同点&#xff1a; 1. 语…...

PNG图片转icon图标Python脚本(简易版) - 随笔

摘要 在网站开发或应用程序设计中&#xff0c;常需将高品质PNG图像转换为ICO格式图标。本文提供一份高效Python解决方案&#xff0c;利用Pillow库实现透明背景完美保留的格式转换。 源码示例 from PIL import Imagedef convert_png_to_ico(png_path, ico_path, size):"…...

C语言中的宏

1.防止头文件重复包含 1.#pragma once #pragma once 是一个编译器指令&#xff0c;用于防止头文件被重复包含。它的核心作用是通过简单语法替代传统的头文件保护宏&#xff08;#ifndef/#define/#endif&#xff09;&#xff0c;提升代码简洁性和可维护性。 作用详解 防止重复…...

飞拍技术介绍

运动控制探针功能详细介绍 运动控制探针功能详细介绍(CODESYS+SV63N伺服)_伺服探针功能-CSDN博客文章浏览阅读683次。文章浏览阅读1.2k次。本文详细介绍了如何使用汇川AM400PLC通过EtherCAT总线与禾川X3E伺服进行通信。包括XML硬件描述文件的下载与安装,EtherCAT总线的启用…...

Qt进阶开发:QTcpSocket的详解

文章目录 一、QTcpSocket 简介二、常用方法的介绍和使用三、常用的信号函数一、QTcpSocket 简介 QTcpSocket 是 Qt 网络模块中用于实现基于 TCP 协议的客户端通信的类。它提供了一个面向流的接口,允许程序通过套接字连接到远程主机,发送和接收数据。 所属模块:QtNetwork用于…...

React中的状态管理Dva总结

在 React 开发中&#xff0c;随着应用的复杂度增加&#xff0c;如何高效地管理应用状态成为了一个非常重要的问题。为了解决这一问题&#xff0c;很多开发者选择了 Redux&#xff0c;然而 Redux 的学习曲线较陡&#xff0c;且需要配置较多的样板代码。为此&#xff0c;Ant Desi…...

PyTorch中的nn.Embedding应用详解

PyTorch 文章目录 PyTorch前言一、nn.Embedding的基本原理二、nn.Embedding的实际应用简单的例子自然语言处理任务 前言 在深度学习中&#xff0c;词嵌入&#xff08;Word Embedding&#xff09;是一种常见的技术&#xff0c;用于将离散的词汇或符号映射到连续的向量空间。这种…...

Python Django基于模板的药品名称识别系统【附源码、文档说明】

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…...

深度学习---获取模型中间层输出的意义

一、什么是 Hook&#xff08;钩子函数&#xff09;&#xff1f; 在 PyTorch 中&#xff0c;Hook 是一种机制&#xff0c;允许我们在模型的前向传播或反向传播过程中&#xff0c;插入自定义的函数&#xff0c;用来观察或修改中间数据。 最常用的 hook 是 forward hook&#xf…...

【软件测试】第一章·软件测试概述

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;软件测试与软件项目管理_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录…...

格雷希尔G10和G15系列自动化快速密封连接器,适用于哪些管件的密封,以及它们相关的特性有哪些?

格雷希尔G10和G15系列快速密封连接器&#xff0c;用于自动化和半自动化过程中的外部或内部密封&#xff0c;通过使用气压驱动来挤压内部的密封圈&#xff0c;创造一个适用于各种管件的无泄漏密封连接&#xff0c;连接器内部的弹性密封圈可以提供其他产品不能提供的卓越密封性能…...

从零实现一个高并发内存池 - 1

C 高性能内存池解析 在 C 开发中&#xff0c;内存管理一直是影响程序性能的关键因素之一。传统的内存分配方式如 malloc 和 free 在高并发场景下往往存在性能瓶颈。为了解决这一问题&#xff0c;很多优秀的内存池方案应运而生&#xff0c;其中 Google 的 tcmalloc&#xff08;T…...

在 STM32 上使用 register 关键字

在 STM32 上使用 register 关键字时&#xff0c;有一些特别需要注意的事项。虽然 register 是 C 语言中的一个标准关键字&#xff0c;它用于提示编译器将变量尽可能存放在寄存器中&#xff0c;以加速访问&#xff0c;但在 STM32 这样的嵌入式平台上&#xff0c;还是需要考虑几个…...

【入门】纸盒的最大体积是多少?

描述 在一张尺寸为 n * n 厘米的正方形硬纸板的四个角上&#xff0c;分别裁剪掉一个 m * m 厘米的小正方形&#xff0c;就可以做成一个无盖纸盒&#xff0c;请问这个无盖纸盒的最大体积是多少&#xff1f; 立方体的体积 v 底面积 * 高&#xff09; 比如&#xff1a; n 5 &am…...

漏桶算法的实际应用案例:数据库批量写入流量控制

场景描述 假设有一个物联网平台&#xff0c;需要实时接收成千上万台设备上报的数据&#xff08;如温度、湿度等&#xff09;&#xff0c;并将数据存入数据库。设备可能在某些时刻集中发送数据&#xff08;例如定时批量上报&#xff09;&#xff0c;直接写入数据库可能导致以下问…...

c++函数参数列表后面的const的作用(常量成员函数)

c函数参数列表后面的const的作用 class Person { public://普通构造函数Person():age(0){}//常量成员函数void read_age() const {age100; //错误&#xff01;常量成员函数不允许修改成员变量&#xff08;除非成员变量用mutable修饰&#xff09;std::cout << "age:…...

Java的While循环写的出票简单程序

import java.util.Scanner;public class Hello {public static void main(String[] args) {Scanner in new Scanner(System.in);int balance 0;while(true){System.out.print("请投币: ");int amount in.nextInt();balance balance amount;if(balance >10 )…...

时间序列基础【学习记录】

文章目录 1. 时间序列中的标签2. 时间序列中的窗口分割器2.1.概述2.2.窗口分割器demo 3. 时间序列的数据加载器3.1.概述3.2.时间序列的dataset3.3.Tensor类型3.4.测试完整流程demo 1. 时间序列中的标签 在目标检测领域的数据集中的图像会有一个标签**(标记一个物体是猫还是狗或…...

Andorid之TabLayout+ViewPager

文章目录 前言一、效果图二、使用步骤1.主xml布局2.activity代码3.MyTaskFragment代码4.MyTaskFragment的xml布局5.Adapter代码6.item布局 总结 前言 TabLayoutViewPager功能需求已经是常见功能了&#xff0c;我就不多解释了&#xff0c;需要的自取。 一、效果图 二、使用步骤…...

光谱相机的光电信号转换

光谱相机的光电信号转换是将分光后的光学信息转化为可处理的数字信号的核心环节&#xff0c;具体分为以下关键步骤&#xff1a; 一、分光后光信号接收与光电转换 ‌分光元件作用‌ 光栅/棱镜/滤光片等分光元件将入射光分解为不同波长单色光&#xff0c;投射至探测器阵列表面…...

MySQL历史版本下载及安装配置教程

1、访问官网下载mysql https://dev.mysql.com/downloads/mysql/ 2、找到历史版本 我这里的版本是最新的mysql8.0.42 下载完成之后,将压缩包进行解压 3、环境变量 在系统变量中找到Path,点击进入编辑&#xff0c;然后依次点击确定退出即可 注意&#xff01;&#xff01;&am…...

【ArcGIS】根据shp范围生成系列等距点:范围外等距点+渔网点(Python全代码)

【ArcGIS】根据shp范围生成系列等距点 目标1&#xff1a;生成边界外一定范围、并且等间距分布的点&#x1f4c1; 所需数据&#xff1a;操作步骤-ArcGIS代码处理-Python 目标2&#xff1a;生成等距渔网点&#x1f4c1; 所需数据&#xff1a;代码处理-Python 参考 目标1&#xff…...

基于FPGA的视频接口之千兆网口(六GigE纯逻辑)

协议简介 相信大家只有对于GigE有所了解的读者,才能找到这篇文章,所谓的GigE协议包含两个方面分别是视频协议(GVSP)和控制协议(GVCP)。 在本文我们重点讲的是基于FPGA纯逻辑实现阉割版的GigE协议,也就是说在很多视频传输过中,只用到了视频流传输,并没有控制方面的要求…...

android 权限配置

在AOSP 14的ROM定制中&#xff0c;<exceptions>和<privapp-permissions>是用于管理特权应用权限的两种不同机制&#xff0c;主要区别在于作用范围、配置方式和权限授予逻辑。以下是具体分析&#xff1a; 1. <privapp-permissions> 标签 作用&#xff1a; 用…...

广告推荐算法入门 day1 --项目选型

文章目录 0 前言1 广告推荐的基本流程2 场景和baseline初步框定2.1召回场景2.2排场景2.3精排场景 3 一个入门小例子感受--淘宝用户购物行为数据可视化分析3.1 数据集介绍3.2 数据分析目标1.时间维度2.产品维度3.行为维度4.用户维度 4 基础项目选型4.1场景 推荐资料后记 0 前言 …...

【Qt】之音视频编程2:QtAV的使用篇

QtAV 基本播放控制功能实现&#xff08;C & QML&#xff09; QtAV 提供了完整的播放控制 API&#xff0c;支持 播放、暂停、停止、快进快退、截屏 等功能。以下是具体实现方法&#xff1a; 1. C 控制方式 基本播放控制 #include <QtAV> #include <QtAV/AVPlaye…...

技术视角下的TikTok店铺运营:从0到1的5个关键点

在当今数字化时代&#xff0c;TikTok Shop作为新兴的电商平台&#xff0c;为众多商家带来了新的机遇。从技术运营的角度来看&#xff0c;以下5个关键点是每个TikTok店铺运营者都需要注意的&#xff1a; 1、规则先行&#xff0c;技术助力合规开店 地区选择&#xff1a;技术分析显…...

机器学习 --- 特征工程(一)

机器学习 — 特征工程&#xff08;一&#xff09; 文章目录 机器学习 --- 特征工程&#xff08;一&#xff09;一&#xff0c;特征工程概念二&#xff0c;特征工程API三&#xff0c;DictVectorizer 字典列表特征提取四&#xff0c;CountVectorizer 文本特征提取4.1 API4.2 英文…...

cocos creator 3.8 下的 2D 改动

在B站找到的系统性cocos视频教程,纯2D开发入门,链接如下: zzehz黑马程序员6天实战游戏开发微信小程序&#xff08;Cocos2d的升级版 CocosCreator JavaScript&#xff09;_哔哩哔哩_bilibili黑马程序员6天实战游戏开发微信小程序&#xff08;Cocos2d的升级版 CocosCreator Ja…...

2025-05-13 表征学习

表征学习 表征学习&#xff08;Representation Learning&#xff09;&#xff0c;又称特征学习&#xff0c;是机器学习领域中的一类技术&#xff0c;旨在自动的从原始数据中学习处有效的特征表示&#xff0c;使得后续的机器学习任务&#xff08;分类、聚类、预测&#xff09;能…...

【WebApi】YiFeiWebApi接口安装说明

YiFeiWebApi接口安装说明 一、 数据库配置文件修改二、 IIS环境配置(建议IIS7.0)三、 安装.NET 8.0 运行时四、 IIS配置站点五、 发布系统六、 测试接口七、测试服务器站点接口八、其他问题查看日志解决九、ApiPost项目文档 一、 数据库配置文件修改 说明&#xff1a; DSCSYSSq…...

亚马逊云科技:开启数字化转型的无限可能

在数字技术蓬勃发展的今天&#xff0c;云计算早已突破单纯技术工具的范畴&#xff0c;成为驱动企业创新、引领行业变革的核心力量。亚马逊云科技凭借前瞻性的战略布局与持续的技术深耕&#xff0c;在全球云计算领域树立起行业标杆&#xff0c;为企业和个人用户提供全方位、高品…...

爬虫请求频率应控制在多少合适?

爬虫请求频率的控制是一个非常重要的问题&#xff0c;它不仅关系到爬虫的效率&#xff0c;还涉及到对目标网站服务器的影响以及避免被封禁的风险。合理的请求频率需要根据多个因素来综合考虑&#xff0c;以下是一些具体的指导原则和建议&#xff1a; 一、目标网站的政策 查看网…...

Rimworld Mod教程 武器Weapon篇 近战章 第二讲:生物可用的近战来源

本讲分析的是在原版&#xff08;coreall dlc&#xff09;环境下&#xff0c;一个Pawn可以用的Tools的所有来源。 重点要分析的是RimWorld.Pawn_MeleeVerbs下的方法GetUpdatedAvailableVerbsList&#xff0c;我把它贴在下面&#xff1a; public List<VerbEntry> GetUpdat…...

SAP汽配解决方案:无锡哲讯科技助力企业数字化转型

汽配行业面临的挑战与机遇 随着汽车行业的快速发展&#xff0c;汽配企业面临着激烈的市场竞争、供应链复杂化、成本压力增大等多重挑战。传统的管理模式已难以满足现代汽配企业对高效生产、精准库存、快速响应的需求。在此背景下&#xff0c;数字化转型成为汽配企业的必然选…...

day19-线性表(顺序表)(链表I)

一、补充 安装软件命令&#xff1a; sudo apt-get install (软件名) 安装格式化对齐&#xff1a;sudo apt-get install clang-format内存泄漏检测工具&#xff1a; sudo apt-get install valgrind 编译后&#xff0c;使用命令 valgrind ./a.out 即可看内存是…...

里氏替换原则:Java 面向对象设计的基石法则

一、原则起源与核心定义 20 世纪 80 年代&#xff0c;计算机科学家芭芭拉・里氏&#xff08;Barbara Liskov&#xff09;在一篇论文中首次提出了里氏替换原则&#xff08;Liskov Substitution Principle&#xff0c;LSP&#xff09;&#xff0c;这成为面向对象设计的重要理论基…...

GBK与UTF-8编码问题(2)

1. 问题现象 在python代码中&#xff0c;用open函数打开文本文件并显示文本内容&#xff0c;中文显示乱码&#xff0c;代码如下。 from tkinter import * import tkinter.filedialogroot Tk() # 给窗口的可视化起名字 root.title(Open File Test)# 设定窗口的大小(长 * 宽) r…...

项目三 - 任务6:回文日期判断

本任务通过判断回文日期&#xff0c;深入学习了Java中日期和字符串处理的相关知识。通过输入年、月、日&#xff0c;生成8位日期字符串&#xff0c;利用StringBuffer的reverse()方法反转字符串&#xff0c;比较原字符串与反转后的字符串是否一致&#xff0c;从而判断是否为回文…...

从零开始掌握FreeRTOS(1)移植到STM32

目录 提前准备 源码文件移植 修改 stm32f10x_it.c 修改 FreeRTOS.h 本章思维导图。 提前准备 学习 FreeRTOS 的第一步就是有一份最工程能够跑在 STM32 上。本篇将记录本人从0搭建一个最基础的移植工程Demo。 要完成这份 Demo&#xff0c;首先我们需要预先准备以下东西&…...

esp32硬件支持AT指令

步骤1&#xff1a;下载AT固件 从乐鑫官网或Git鑫GitHub仓库&#xff08;https://github.com/espressif/esp-at&#xff09;获取对应ESP32型号的AT固件&#xff08;如ESP32-AT.bin&#xff09;。 步骤2&#xff1a;安装烧录工具 使用 esptool.py&#xff08;命令行工具&#…...