PL/0编译原理课程设计:从词法分析到代码生成的修改扩充全攻略
简介编译原理课程设计中针对PL/0编译器进行修改与扩充的完整工程包覆盖赋值运算符、-以及Pascal风格FOR、DOWNTO循环语句等核心语法改造适合计算机专业学生做编译原理实验或课设时参考。压缩包共42个文件包含PL0源程序、COD中间代码、CPP与PAS工程源码、可运行EXE、测试用例及编译原理.doc说明文档整体仅1.25MB结构清晰便于按模块对照练习。已有1053人学习下载。包内可直观看到与--运算的符号表扩展、一维数组类型对应指令的添加方式并能直接运行PL01.exe验证修改效果同时附带多个PL0测试程序与COD文件便于逐步调试语法分析和目标代码生成环节对理解递归下降分析和PL/0虚拟机机制很有帮助。1. 编译原理课程设计里修改扩充PL/0到底改的是什么编译原理课程设计里“对PL/0作出修改扩充”是经典到不能再经典的方向。PL/0这套教学用小型编译程序代码量不算大却把词法分析、语法分析、目标代码生成、解释执行四条链路完整串在了一个工程里。很多人拿到它的第一反应是“往里加几个关键字、加几个运算符”觉得这是往容器里塞东西的小工程真正动手才发现你改词法识别规则时语法分析器不一定报错目标代码生成却可能悄悄出问题变量被覆盖、循环跳错地址、比较结果永远为假这类问题能让人查一整晚。这篇文章想做的事很明确把PL/0修改扩充拆成可复现的步骤从最小改动一路做到控制结构再把符号表、回填地址、词法预读这几类经典坑提前摆出来。适合正在准备课程设计的人也适合那些已经拿到基础代码、却在“到底从哪里下手”上卡住的人。先看懂结构再定改什么最后动手顺序反了扩充就变成了碰运气。2. 看懂PL/0的整体结构词法、递归下降、符号表怎么协同2.1 PL/0的四段结构从字符流到指令流每个环节都有接口PL/0之所以适合做修改扩充是因为它的四个阶段界限清晰而且每个阶段的输出就是下一个阶段的输入。词法分析器负责把源程序字符流切成token流每个token带上类型和值语法分析器用递归下降方式吃token流每识别出一个语法结构就生成一段目标指令目标指令不是真实机器码而是一组面向栈的伪指令最终由一个解释器逐条执行。符号表则贯穿语法分析全程记录常量、变量、过程的名字、种类、层级和数据段偏移。在实际代码里这四段通常集中在一个或几个源文件中。词法部分往往是一个逐字符扫描的函数遇到字母开头走标识符分支遇到数字走数值分支遇到运算符走符号分支语法部分是一组以block、statement、condition、expression、term、factor命名的递归函数目标代码部分则是一个不断追加指令到数组里的生成器外加一个按指令分发的解释器。弄清这组函数之间的调用关系比你背熟任何一条指令的编码都重要。拿到一份没看过的基础代码时我的习惯是先用grep把四段程序的入口函数定位出来而不是从头通读。基本命令是这样grep -n symbol_type\|typedef.*sym source.c # 找token类型定义 grep -n getSym\|getsym\|nextSym source.c # 找词法分析主函数 grep -n void block\|void statement\|void condition\|void expression source.c # 找递归下降函数 grep -n case.*LIT\|case.*JMP\|case.*OPR\|switch codegen.c # 找指令生成与解释器这段命令的作用是先把“改哪里”的地图画出来。不同教材、不同人改写的PL/0函数命名会有差异比如词法函数有的叫getsym有的叫next_symbol你只要按注释和命名习惯微调关键词就行。定位完之后在纸上画一张调用关系草图标出词法返回的token类型在哪里被语法函数消费、指令数组在哪里被追加、符号表的偏移字段在哪个结构体里后面所有扩充都围绕这张图展开。2.2 扩充的三个档位先定规模再定路线对PL/0做修改最怕一上来就选一个“看起来酷”的目标比如加数组、加字符串、加指针。这类扩充不是加一个case就能完事的它要求词法、语法、符号表、代码生成、解释器五处同步修改任何一处漏掉都会出现极其隐蔽的运行时错误。我一般把课程设计里常见的扩充分成三个档位按风险和难度排好序。扩充档位典型目标需要动的模块难度主要风险第一档新增比较运算符、取模、自增自减词法分析、条件解析低词法预读丢字符、运算符优先级混入第二档REPEAT-UNTIL、FOR循环、CASE语句词法、语法、代码生成、回填地址中回填地址错误、嵌套循环跳错目标第三档布尔类型、数组、字符串、函数返回值词法、语法、符号表、代码生成、解释器高符号表偏移错位、类型宽度未乘入存取指令选型理由很简单第一档只会引入新的token类型和新的解析分支不改变符号表结构也不改变指令格式翻车点最少适合用来建立对整个编译链路的信心第二档必须处理跳转地址的回填这是编译原理课程设计里最有教学价值的一环能把语法分析和代码生成的联动看明白第三档则要动符号表的偏移分配逻辑过程中出现变量互相覆盖这类玄学问题的概率极高。所以如果时间只有两周我建议老老实实做到第二档。如果基础好、时间充裕第三档的数组扩充也值得碰但前提是你已经把第二档的回填机制吃透了。扩展的每一步只引入一个新变量这个“新变量”可以是新token、新产生式或新指令一次只动一类出了问题就知道去哪个模块里查。3. 从最小改动起步给PL/0加 和 ! 运算符的完整改法3.1 为什么从比较运算符起步这是一条看得见全链路的改动在PL/0的基础上新增和!是三个档位里最不吓人、却能让你完整走一遍“识别—解析—生成指令”全流程的改动。PL/0原有的相等判断用的是单个:才是赋值这个语义要保留。很多人扩充时不加新token而是直接把改成赋值的含义结果原有程序里所有相等判断全部翻车这是最经典的自杀式改法。比较运算符的语义落点也很简单if (a b)中间的首先要被词法分析器识别成一个独立token类型然后语法分析器在解析条件表达式时遇到这个token要能接受它并继续解析右侧表达式最后生成一条比较指令或条件跳转指令。三层改动对应着三个明确的修改位置任何一个位置漏改编译时都会在语法分析阶段报错或产生错误的比较结果。3.2 词法分析先让“”和“!”被编译器看见词法分析器是所有改动的第一道关口。如果词法阶段没识别出语法分析器永远不可能看到一个叫“相等”的token后续一切无从谈起。首先要在token类型定义里追加两个新枚举值放在原有比较运算符附近便于阅读/* new_token.h —— 在原有 symbol 类型定义末尾追加 */ typedef enum { SYM_NULL, SYM_IDENTIFIER, SYM_NUMBER, SYM_PLUS, SYM_MINUS, SYM_TIMES, SYM_SLASH, SYM_EQUAL, /* 原有相等比较 */ SYM_ASSIGN, /* 原有赋值 : */ SYM_EQ, /* 新增相等 */ SYM_NEQ, /* 新增不等 ! */ SYM_LEQ, SYM_LESS, SYM_GEQ, SYM_GREATER, SYM_ODD, SYM_LPAREN, SYM_RPAREN, SYM_COMMA, SYM_SEMICOLON, SYM_PERIOD /* 各保留字由关键字表映射不在枚举里逐项展开 */ } symbol_type;紧接着在词法分析主循环里增加对和!两个字符的预读处理。核心是先取当前字符再取下一个字符两个字符拼起来判断而不是只判断一个字符就推进指针。下面是一段类C的写法你可以直接对照自己手里的实现改/* next_symbol() 词法主循环中的一个分支 */ if (ch ) { ch next_char(); /* 预读下一个字符 */ if (ch ) { cur_sym SYM_EQ; /* 双等号新token */ ch next_char(); /* 吃掉第二个 */ } else { cur_sym SYM_EQUAL; /* 单等号保留原有比较语义 */ } } else if (ch !) { ch next_char(); if (ch ) { cur_sym SYM_NEQ; ch next_char(); } else { error(! 后面必须紧跟 ); } }这里有两个参数细节值得说明。第一token枚举里新增项的先后顺序不要随意调整因为有些PL/0实现用整数下标映射保留字表调整顺序会导致保留字识别错乱。第二词法分析里的预读指针一旦多推进一步下一个token的首字符就会丢失表现为“标识符变成空串”“数字少了首位”这类问题几乎都是ch next_char()的位置放错了。写完这段建议立刻打印一份token流确认a b被切成了标识符 / / 标识符三段。3.3 语法分析与代码生成把接到条件解析上PL/0的条件语法一般长这样condition :: odd exp | exp relop exp。原来的relop集合里已经有、#、、、、现在要把SYM_EQ和SYM_NEQ补充进这个集合。在递归下降的condition()函数里改动点非常集中void condition() { if (cur_sym SYM_ODD) { get_symbol(); expression(); code_gen(OPR_ODD, 0, 0); /* 奇偶判断指令参考原有实现 */ } else { expression(); /* 解析左操作数 */ if (cur_sym SYM_EQ || cur_sym SYM_NEQ || cur_sym SYM_LESS || cur_sym SYM_LEQ || cur_sym SYM_GREATER || cur_sym SYM_GEQ) { symbol_type op cur_sym; /* 记住当前比较符 */ get_symbol(); expression(); /* 解析右操作数 */ code_gen(OPR_COMPARE, 0, map_relop(op)); /* 生成比较指令 */ } else { error(condition 中缺少合法的比较运算符); } } }这段代码里的关键是map_relop(op)它把token类型映射成解释器能认的操作码。映射方式不唯一常见做法是沿用原有和#的指令编码把SYM_EQ映射到等值判断SYM_NEQ映射到不等判断如果你的实现里#本来就是“不等”的意思直接让SYM_NEQ走它的分支即可。解释器侧的处理是比较栈顶两个值按操作码置0或1随后由条件跳转指令消费这个结果。一个比较重要的语义问题PL/0里原本就是相等比较不是赋值。所以新增时选择“映射到原有相等指令单继续保留原有语义”是最省事的做法如果你非要让和新写的指令绑定那就必须在解释器里新增一条指令分支否则会落到默认处理里直接报错。3.4 用一个最小程序验证链路是否打通验证这一步不需要复杂的测试框架直接写一个几行的PL/0源程序跑通后看输出。我的做法是正例和反例各一条确保两条路径都覆盖到// test_eq.pl0 —— 正例相等比较 VAR a, b; BEGIN a : 10; b : 10; IF a b THEN write(1) END.// test_neq.pl0 —— 反例不等比较 VAR a, b; BEGIN a : 10; b : 20; IF a ! b THEN write(1) END.如果两个程序都输出1说明从词法识别到语法挂接再到指令生成这条链路是通的。如果正例输出0或空优先检查解释器里比较指令的处理是否正确——很多PL/0实现里比较指令会把结果反着存也就是相等时置0这时的语义就恰好颠倒了。这个问题在加运算符的扩充里非常典型你只需要把解释器里条件判断的取值逻辑对调一下即可。4. 控制结构扩充REPEAT-UNTIL 的递归下降与回填4.1 为什么REPEAT-UNTIL值得做它逼你搞懂回填控制类扩充里REPEAT-UNTIL是课程设计里最讨巧也最有分量的一个选择。WHILE循环是先判断后执行递归下降天然支持先解析条件生成条件跳转再解析语句体最后回跳。REPEAT-UNTIL恰好反过来它是先执行一次循环体再判断条件条件为假时继续循环为真时退出。这意味着循环入口地址在语句体之前就确定了而判断条件的代码在循环体末尾条件跳转指令的目标地址在生成指令时还没办法直接写死。这就是“回填”的由来你先在指令序列里占一个位置指令的目标地址暂时填0等解析完条件、确定循环入口地址之后再回过头把真实的地址写进那条跳转指令。很多人在这一步卡住原因不是不懂回填概念而是没弄清楚PL/0指令数组是按顺序追加的、地址就是数组下标。你不需要为循环单独维护一个“地址变量”只需要在循环入口处记录当时的指令下标然后在生成跳转指令时把它作为目标地址填回去。4.2 递归下降里怎么处理REPEAT-UNTIL的语法PL/0风格的REPEAT-UNTIL语法可以写成repeat_stmt :: REPEAT statement { ; statement } UNTIL condition。递归下降解析时按这个产生式逐步推进即可。下面是一段可对照修改的类C解析函数/* 假设已有指令列表的当前长度函数 cur_instr_addr() */ /* code_gen(op, arg1, arg2) 会向指令数组追加一条指令并返回该指令的下标 */ void parse_repeat() { int loop_entry cur_instr_addr(); /* 1. 记录循环体入口地址 */ get_symbol(); /* 2. 吃掉 REPEAT 关键字 */ do { statement(); /* 3. 解析一条循环体语句 */ if (cur_sym SYM_SEMICOLON) { get_symbol(); /* 多条语句用分号分隔 */ } else { break; } } while (cur_sym ! SYM_UNTIL); if (cur_sym ! SYM_UNTIL) { error(REPEAT 缺少配对的 UNTIL); return; } get_symbol(); /* 4. 吃掉 UNTIL 关键字 */ condition(); /* 5. 解析循环条件 */ code_gen(JPC, 0, loop_entry); /* 6. 条件为假时跳回循环入口 */ }这个函数的执行顺序是精心安排的每一条注释对应一个不能省略的步骤。第1步必须在解析任何循环体语句之前执行因为statement()内部还会继续向指令数组追加内容一旦先解析语句再记录地址循环入口就变成了第二条语句的地址。第6步的JPC指令语义是“条件为假时跳到目标地址”这个方向正好匹配REPEAT-UNTIL的语义条件成立顺序往下走循环结束条件不成立跳回入口继续循环。如果写成“条件为真跳回”循环就会在条件第一次成立时死循环。参数层面JPC的第二个参数字段通常带一个立即数这里直接放loop_entry。如果你的PL/0实现有自己的跳转指令命名比如JMP、JIN之类保留原有助记符只改目标地址的来源即可。重点是loop_entry取值先于循环体解析JPC的生成晚于条件解析这个先后顺序决定回填正确与否。4.3 嵌套REPEAT用入口地址栈处理循环层级加了REPEAT-UNTIL之后最普遍的翻车点出现在嵌套场景。内层REPEAT的入口地址和外层REPEAT的入口地址都是同一个变量loop_entry存储的内层解析完loop_entry被覆盖外层最后回填时就只能拿到内层的入口。解决方法是把循环入口地址压栈而不是放局部变量或者更彻底一点用一个全局入口地址数组下标随嵌套深度变化。int loop_stack[64]; /* 循环入口地址栈深度按代码里的嵌套层数调整 */ int loop_top 0; void parse_repeat() { loop_stack[loop_top] cur_instr_addr(); /* 入口压栈 */ /* ...省略循环体解析过程... */ condition(); int entry loop_stack[--loop_top]; /* 弹栈取本层入口 */ code_gen(JPC, 0, entry); }这段代码里的loop_top必须在每次解析REPEAT时递增在生成JPC后递减。如果漏了递减下一次REPEAT解析时top会继续累加短时间内栈溢出如果漏了递增嵌套循环的内层和外层会拿到同一个入口地址循环行为直接错乱。建议在每次生成JPC后调试打印一条[repeat] entryxxx日志对照指令数组的下标检查回填值是否落在REPEAT后第一条指令的位置。这种入口栈的办法同样适用于WHILE和REPEAT混用的场景。如果你的扩充不止REPEAT还加了FOR、CASE把它们的跳转入口统一压进同一个栈解析出口时统一弹栈能避免为每种结构单独维护一套地址变量的麻烦。唯一的代价是栈的大小需要根据嵌套深度估算课程设计层面开64就绰绰有余了。4.4 验证REPEAT用计数程序确认循环次数和跳转方向控制结构的验证不能只看编译能不能过要看执行次数。用下面这个程序跑一遍期望输出应该是1234也就是变量i从1加到4最后一次进入循环体时i等于4条件i 3成立循环退出// test_repeat.pl0 —— 验证 REPEAT-UNTIL 循环次数 VAR i; BEGIN i : 0; REPEAT i : i 1; write(i) UNTIL i 3 END.如果输出是123就退出说明条件判断方向反了循环在i等于3时因为条件已经成立而提前终止。如果输出是12345或更长说明JPC跳转目标回填到了循环体内部的某条指令而不是循环入口。这两种情况对应的排错思路完全不同前者查解释器里条件跳转指令的取值逻辑后者查loop_entry记录的位置是否在循环体第一条指令之前。跑完这个用例再套一层REPEAT嵌套测试能进一步确认入口栈的逻辑是否正确。5. PL/0修改扩充的避坑清单词法、符号表与回填的三类翻车现场5.1 局部变量互相覆盖改完值总是“差一位”现象新增数组或新的变量声明方式后过程里声明的局部变量A的值总被变量B覆盖打印A得到的永远是B的值。原因符号表在给每个变量分配数据段偏移时发生了重复两个变量拿到了同一个偏移量。解决检查符号表里偏移分配的逻辑确保每增加一个符号项都调用统一的分配函数不要手工为“特殊变量”指定偏移。调试时在填表后打印每个变量的名字和偏移值一眼就能看出哪两个变量撞了偏移。5.2 嵌套REPEAT回填错乱内层循环把外层入口当目标现象两层REPEAT嵌套时外层循环只执行一次或者内层循环结束后程序直接跳过外层剩余部分。原因内外层循环共用了同一个loop_entry变量内层解析结束后覆盖了外层的入口地址。解决把循环入口地址改成栈结构每个REPEAT解析时压栈生成跳转指令时弹栈。血泪经验不要为了省事把loop_entry声明成函数内的静态变量静态变量在递归调用里同样会被内层覆盖。5.3 把“”改成赋值语义原有相等判断全部翻车现象扩充运算符时想让承担赋值功能结果原有程序里所有IF a b THEN都变成了给b赋值条件恒真或恒假。原因PL/0原有的是相等判断语义:才是赋值修改赋值规则属于破坏性改动。解决让新增的负责相等比较保留原有语义。如果你确实需要单等号赋值那也要同步把所有源程序里旧的相等用法改成这不是代码改动而是语言语义重构课程设计中完全没必要。5.4 词法预读多推进一位下一个token的首字符丢失现象源程序里a : 10; b : 20;解析完a之后下一个标识符变成空串或者数字20解析成0。原因词法分析器处理新增运算符时多调用了一次字符推进函数导致预读指针越过下一个token的首字符。解决逐字符核对词法分析流程确保每个分支对预读字符的推进次数一致。打印token流是最快的排查手段token流里出现空标识符问题一定出在预读逻辑而不是语法分析。5.5 新增数组类型后存取指令没有按类型宽度缩放现象定义数组类型的变量后给第二个元素赋值读出来却是第一个元素的值或乱码。原因解释器执行load/store指令时按单元存取没有把下标乘上单个元素的宽度于是偏移计算全部错位。解决在符号表里记录每个变量的类型宽度代码生成时对引用类型的变量统一乘上get_size(type)。这个坑坑在“你只改了语法和符号表忘了改代码生成”属于典型的三档扩充翻车点。6. 用边界测试验证改动把回归习惯变成默认动作改完PL/0的扩充千万不要拿一个测试程序跑通就结束。我的习惯是维护一份固定的测试用例集每个扩充方向配四类样例最小正例、最小反例、边界用例、旧功能回归。最小正例证明新功能确实被识别最小反例证明非法输入会被拒绝边界用例暴露回填和比较方向的问题旧功能回归则是保护伞——防止你在改REPEAT时把WHILE弄坏。这套测试集不需要任何框架一个Shell脚本加几个.pl0文件就够。#!/bin/bash # regression_test.sh —— 对 PL/0 扩充版本做回归验证 # 用法: ./regression_test.sh 编译产物路径 测试用例目录 PL0_BIN$1 TEST_DIR$2 PASS_COUNT0 FAIL_COUNT0 for f in $TEST_DIR/pass_*.pl0; do if $PL0_BIN $f /tmp/pl0_out.txt 21; then echo [PASS] $f 编译并执行成功 PASS_COUNT$((PASS_COUNT 1)) else echo [FAIL] $f 返回非零错误信息如下 cat /tmp/pl0_out.txt FAIL_COUNT$((FAIL_COUNT 1)) fi done for f in $TEST_DIR/fail_*.pl0; do if $PL0_BIN $f /tmp/pl0_out.txt 21; then echo [FAIL] $f 应当编译失败但实际成功了 FAIL_COUNT$((FAIL_COUNT 1)) else echo [PASS] $f 被正确拒绝 PASS_COUNT$((PASS_COUNT 1)) fi done echo 通过 $PASS_COUNT 项失败 $FAIL_COUNT 项脚本里的pass_*.pl0放应该通过的用例fail_*.pl0放应该拒绝的用例。PL0_BIN是编译产物的路径TEST_DIR是测试目录两个参数都从命令行传入方便在不同实现间复用。脚本只检查退出码要检查输出值是否正确另配一个expected_output文件做diff目标更加明确。命名规则里pass_和fail_前缀就是筛选条件不匹配这两个前缀的文件会被忽略方便你放一些临时草稿用例。我自己做这类扩充时吃过一次大亏某次为了赶进度在一份没有版本管理的代码上直接改符号表结构改到一半发现原有测试程序全线崩溃又找不到上一次能跑的版本最后只能连夜手工还原。那次之后我做任何改动前都会先复制一份干净基础代码每次改完跑一遍回归脚本确认所有pass_样例依旧通过、所有fail_样例依旧被拒。这个习惯帮你省下的时间远超复制和跑脚本花掉的时间而且它不只在课程设计里有用。注意比较方向的变化往往不体现在编译错误里而是体现在错误输出上所以diff验证那一步别跳过。希望这些步骤和习惯对你这次PL/0扩充有用。本文还有配套的精品资源点击获取

相关新闻

将Node.js升级到最新版本:用TaoToken统一Key跑通升级后AI工具链

将Node.js升级到最新版本:用TaoToken统一Key跑通升级后AI工具链

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

2026/10/12 7:48:31 阅读更多 →
Zynq裸机UDP通信实战:lwIP发送Hello World与系统不响应问题排查

Zynq裸机UDP通信实战:lwIP发送Hello World与系统不响应问题排查

1. 项目背景与核心需求拆解1.1 这个项目到底在做什么先把场景说清楚。这个项目的核心目标是在Zynq平台上实现一个最基础的网络通信功能:开发板通过UDP协议,向上位机发送一条"Hello World"字符串。听起来简单得不能再简单,但做过Zyn…

2026/10/12 7:47:30 阅读更多 →
汇川H5U PLC程序框架设计:InoProShop任务配置、伺服与气缸封装实战

汇川H5U PLC程序框架设计:InoProShop任务配置、伺服与气缸封装实战

1. H5U这台控制器到底什么来路:硬件定位与选型逻辑先聊点背景。汇川H5U属于中型PLC阵营,定位介于小型AM系列和大型ACAP之间,比较典型的应用场景是锂电设备、3C组装、物流分拣、包装机械这些需要多轴联动但又不至于上高端运动控制卡的场合。很…

2026/10/12 7:47:30 阅读更多 →

最新新闻

数据复制深度拆解:从HDFS块复制到CDC同步的机制、实践与容灾设计

数据复制深度拆解:从HDFS块复制到CDC同步的机制、实践与容灾设计

大数据领域做久了,你会发现一个很朴素但又绕不开的现实: 数据复制 几乎贯穿了所有核心系统的生命周期。无论是离线数仓里的表同步、实时链路里的CDC采集,还是跨机房容灾里的镜像复制,本质上都在做同一件事——把一份数据从A点搬…

2026/10/12 9:23:59 阅读更多 →
开源大模型权重质变:推理优化、量化与微调工程实践指南

开源大模型权重质变:推理优化、量化与微调工程实践指南

1. 从"权重开放"到"能力平权":一场静默的行业拐点 过去两年,我一直在跟踪各类开源模型权重发布后的实际落地情况。说实话,早期大部分所谓"开放权重"的模型,更多是实验室里的展示品——参数量大、推…

2026/10/12 9:23:59 阅读更多 →
配电网多目标Pareto重构:Matlab实现NSGA-II的实战经验与代码解析

配电网多目标Pareto重构:Matlab实现NSGA-II的实战经验与代码解析

配电网多目标Pareto重构,核心就一句话:通过改变分段开关和联络开关的开合状态,重新编排配电网的供电路径,让网损、电压质量、开关操作成本等多个目标同时尽可能好。这个问题我前后做了一段时间,用Matlab实现了多套智能…

2026/10/12 9:23:59 阅读更多 →
把手机变成AI服务器:openclaw-termux SSH远程访问配置实战教程

把手机变成AI服务器:openclaw-termux SSH远程访问配置实战教程

AI 应用LLM 网关大模型移动开发 【免费下载链接】openclaw-termux Run OpenClaw AI Gateway on Android — standalone Flutter app with built-in terminal, web dashboard, and one-tap setup. Also available as a Termux CLI package. 项目地址: https://gitcod…

2026/10/12 9:23:58 阅读更多 →
自己动手实现TreeMultiSet:基于TreeMap的有序可重复集合

自己动手实现TreeMultiSet:基于TreeMap的有序可重复集合

1. 从一个真实的数据统计需求说起去年帮一个做电商的朋友处理订单数据时&#xff0c;遇到一个挺典型的问题&#xff1a;他们需要统计每个商品在一天内的被下单次数&#xff0c;并且要按商品ID排序输出。第一反应是用TreeMap<Long, Integer>&#xff0c;key 是商品ID&…

2026/10/12 9:22:58 阅读更多 →
PS5折腾指南:系统配置、存储扩展、远程游玩与散热维护

PS5折腾指南:系统配置、存储扩展、远程游玩与散热维护

PS5到手三个月后&#xff0c;我发现自己陷入了一个尴尬循环&#xff1a;开机看一眼、随便通关一个进度、然后切出去看手机&#xff0c;主机外壳摸起来都能煎鸡蛋。直到我给自己定了个"AnyPS5"计划——不把这台机器当普通游戏机&#xff0c;而是当成一台可折腾的娱乐终…

2026/10/12 9:22:58 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天&#xff0c;画面可以做到绝对的锐利、平滑与无瑕。然而&#xff0c;当一张秋日手账插画或拍立得照片过于“平整无瑕”时&#xff0c;往往会散发出一种冰冷生硬的“数码塑料感&#xff08;Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中&#xff0c;横排&#xff08;Horizontal Layout&#xff09;早已经成为了绝对的主流。然而&#xff0c;当我们翻开泛黄的线装古籍、宋版木刻诗集&#xff0c;或是欣赏一张茶道雅集的手写便签时&#xff0c;那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点&#xff0c;很多人心里都会悄悄亮起一盏警示灯。 在心理学上&#xff0c;这种现象有一个专门的称谓——“周日夜晚焦虑症&#xff08;Sunday Scaries&#xff09;”。明天又是周一&#xff0c;闹钟又要重新在七点响彻卧房&#xff1b;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/11 14:36:54 阅读更多 →