GNU Make eval函数详解:用模板批量生成Makefile规则,告别重复维护

你的Makefile里是不是也有这种情节:同一个编译规则复制粘贴了十几遍,只改一下模块名和文件路径?我承认我干过,而且是在一个模拟项目的三十多个子模块上干的,每次新增模块都要手动补规则、补依赖、补清理清单,漏一条就让你在深夜对着构建日志发呆。后来真正把GNU Makefile里的eval函数用起来,才找到治本的办法:把规则写成模板,让make自己在解析阶段批量生成。eval的定位很朴素——它接收一段文本,把这段文本当作makefile内容现场解析,配合define、call、foreach,就能实现自动生成规则。这篇文章把从最小例子到生产级坑点的完整路径走一遍,适合正在维护多模块构建、已经受够手写重复规则的人。

1. 从第30条重复规则说起:eval到底帮我省了什么

1.1 手工规则膨胀的典型现场

我维护过一个模拟项目的构建脚本,结构大概是这样的:十几个子模块,每个模块都要先编译成目标文件,再链接成可执行程序,还要有一堆按模块区分的测试入口。手工写下去,Makefile长这样:

build/alpha.o: src/alpha.c $(CC) $(CFLAGS) -c $< -o $@ build/alpha.bin: build/alpha.o $(CC) $(LDFLAGS) $^ -o $@ build/beta.o: src/beta.c $(CC) $(CFLAGS) -c $< -o $@ build/beta.bin: build/beta.o $(CC) $(LDFLAGS) $^ -o $@

模块一多,文件就变成"规则海洋"。问题不在于多写了几行,而在于每次改构建策略都要同步改几十处:某天我把CFLAGS统一加了一个-Werror,就得保证所有编译规则都改到;某个新模块忘记写目录依赖,第一次构建必挂。最要命的是,这类文件没法review出"结构",大家只会逐行看有没有拼错。

复制粘贴还有一个隐形成本:它让"规则的一致性"变成了一种人工约定。只要有一次只改了A模块没改B模块,后续的构建产物就开始悄悄不一致。我后来统计过,手工维护阶段,这类"漏改"导致的构建问题占了一半以上。

1.2 eval在make里的真正位置

简单说,make在执行recipe之前,要先花一段时间把整个makefile"读"进来:解析变量、规则、依赖,构建出一张依赖图,这个过程叫读入阶段。eval就是在读入阶段生效的——它把一段文本现场变成makefile文本,再现场解析。你可以把它理解为在makefile内部装了一个"宏处理器"。

这跟函数式编程里的"代码生成代码"很像:eval先把模板里的占位符换成真实值,生成一段makefile片段,然后这段片段和手写的规则没有任何区别,会被make当成普通规则加入依赖图。所以"生成的规则"和"手写的规则"是平级的,后续的依赖解析、并行构建、make -n试运行,全都一视同仁。

需要注意,这是GNU Make的功能特性。BSD make、nmake这类工具要么没有对应的eval,要么语法完全不同。如果项目明确要求跨多种make工具移植,用eval之前要想清楚,或者评估能否接受"仅支持GNU Make"这个前提。

1.3 什么项目值得上eval:两道判断题

我一般用两个问题来判断:

  • 这些规则是不是同构的?也就是除了名字、路径、参数不同,结构完全一样。
  • 数量是不是大到手动维护会出错?一般来说超过五六个同构目标,就开始值得了。

如果目标少于五个,而且每个规则差异都很大,比如有的要调内置工具、有的要跑外部脚本、有的要特殊后处理,那强行套eval反而把简单问题复杂化。eval最适合的场景是"大量同构目标 + 少量差异化参数",这时候它才能把规则的重复度压到最低,让新增模块变成一行列表项。

团队对GNU Make的接受度也要考虑。如果整个构建体系里已经大量使用了$(call)、$(foreach)这类函数式语法,那么引入eval几乎没有额外学习成本。如果团队还停留在"makefile就是写规则"的认知上,那这篇文章后面的内容反而是最好的入门教材。

2. 先跑通再理解:define、call与make的两遍解析

2.1 最小可复现例子

先从最简单的例子跑通。下面这个makefile定义了一个模板,生成两个目标:

define greeting_rule say_$(1): @echo "hello, $$@" endef $(eval $(call greeting_rule,one)) $(eval $(call greeting_rule,two))

这里有个关键细节:say_$(1):下面那行recipe,缩进必须是真实的Tab字符,不是空格。define/endef会把中间的内容原样保存,包括这个Tab,eval解析时才能认出它是recipe。运行make say_one,你应该看到:

hello, say_one

这短短几行里其实串起了四个概念:define定义多行变量;$(1)是模板参数占位符;$(call ...)把参数传入模板;$(eval ...)把展开后的文本当作makefile内容解析。整条链路就是"模板 -> 替换 -> 解析"。

我把这个例子拆开说,是因为很多人一上来就写复杂模板,结果编译报错也不知道是语法问题还是转义问题。先把最小链路跑通,再往上加东西,排查范围会小很多。

2.2 两遍解析:读入期与执行期

理解eval,必须理解make处理makefile的两遍过程。

第一遍是读入期。make逐行读取makefile,解析变量赋值、规则定义、依赖关系,最终形成一张完整的依赖图。$(eval ...)调用就发生在这一遍:它边生成文本边解析,相当于在读入过程中不断往依赖图里追加内容。

第二遍是执行期。make拿到依赖图之后,检查哪些目标过期,然后按依赖顺序执行recipe。注意,recipe里的变量不是读入期就展开的,而是延迟到执行期、每一条shell命令执行前才展开。这就是"recipe延迟展开"机制。

这两遍分开的代价是:eval的参数本身会被make先展开一次。写$(eval $(call xxx))的时候,make会先把$(call xxx)展开成一段makefile文本,然后才去解析这段文本。所以模板里凡是想"原样留给执行期"的变量引用,都必须多写一个$。这直接引出下面这个最容易被绕晕的转义问题。

2.3 立即展开、延迟展开与那对$$

我见过太多人在eval模板里被$$折磨。其实规则就一条:eval参数展开时,$()会立刻取值;$$()经过展开后蜕变成$(),原样写进解析结果为recipe,留到执行期再展开。

在模板里写eval展开后存入数据库最终执行效果
$(CFLAGS)CFLAGS的当前值生成时快照,后续改动不影响
$$(CFLAGS)文本$(CFLAGS)执行recipe时才取CFLAGS的值
$$@$@执行时自动变量:目标名
$@空字符串读入期没有目标上下文,直接丢掉

最后一行的坑最典型:$@是自动变量,只有执行recipe时才有意义。在eval参数展开阶段,make根本不在任何规则上下文里,$@就是空串。所以模板里的自动变量必须写成$$@、$$<、$$^。我的经验法则是:模板里所有希望最终在recipe里展开的东西,一律写成$$开头,宁可多转义,不要少转义。

3. 实战:从源文件列表批量生成编译规则

3.1 foreach + call + eval三连的完整骨架

现在把前面几块拼起来,做一个真正能用的编译规则生成器。假设有三个模块alpha、beta、gamma,每个模块有一个源文件:

APPS := alpha beta gamma define app_rule build/$(1).o: src/$(1).c | build_dir $$(CC) $$(CFLAGS) -c $$< -o $$@ build/$(1).bin: build/$(1).o $$(CC) $$(LDFLAGS) $$^ -o $$@ endef $(foreach app,$(APPS),$(eval $(call app_rule,$(app)))) build_dir: mkdir -p build .PHONY: build_dir

$(foreach app,$(APPS),...)的语义是:遍历APPS列表,每轮把当前值赋给变量app,并展开循环体文本。展开体里调用了$(call app_rule,$(app)),于是每个模块名都会被代入模板,生成两到三行规则文本,最后交给$(eval)解析。

这么一来,APPS列表成了唯一的事实来源。新增一个模块,只需要在列表里加一行,编译规则、链接规则、目录依赖全部自动生成。我把这个模式叫作"三连":foreach负责遍历,call负责传参,eval负责落地。

| build_dir是order-only前置依赖,意思是"构建前必须先执行build_dir的recipe,但它的时间戳变化不触发本目标重编"。目录这种资源,时间戳每构建一次都会变,如果写成普通依赖,会导致所有目标每次都被判为过时,那是纯粹浪费。这个细节别忽略。

3.2 把差异化参数收进模板:每模块一套编译选项

同构规则最常见的变体是"编译参数不一样"。比如alpha要开-O2和宏定义,beta要开-g做调试。手工阶段,这类差异是靠复制粘贴不同CFLAGS行实现的;用eval,可以把参数做成"每模块一个变量",模板里按目标名拼变量名:

APPS := alpha beta CFLAGS_alpha := -O2 -DALPHA CFLAGS_beta := -O0 -g -Wall define app_rule obj/$(1).o: src/$(1).c $$(CC) $$(CFLAGS_$(1)) -c $$< -o $$@ endef $(foreach app,$(APPS),$(eval $(call app_rule,$(app))))

注意$$(CFLAGS_$(1))的处理顺序:$(call)先把$(1)替换成模块名,比如alpha,模板就变成$$(CFLAGS_alpha);eval参数展开时,$$蜕变成$,存进数据库的是$(CFLAGS_alpha);等到执行recipe时,才真正去读CFLAGS_alpha变量的值。所以这个模板既捕获了"每模块不同参数"的差异,又保持了"参数可以后定义"的灵活性——CFLAGS_alpha放在foreach前还是后,都不影响最终结果。

这套写法比"每个模块复制一行CFLAGS"干净得多。以后想看某模块用了什么编译选项,直接看变量定义区;想统一加选项,改一处全局的也没问题。

3.3 顺手生成目录规则、clean入口与all收口

eval还有一个经常被忽略的能力:它生成的文本里可以包含普通的变量赋值语句。也就是说,eval不仅能生成规则,还能在生成的同一次解析中执行TARGETS += ...这类赋值。利用这一点,我们可以让模板自动收集自己生成的目标:

TARGETS := define app_rule bin/$(1): obj/$(1).o $$(CC) $$(LDFLAGS) $$< -o $$@ obj/$(1).o: src/$(1).c $$(CC) $$(CFLAGS) -c $$< -o $$@ TARGETS += bin/$(1) endef $(foreach app,$(APPS),$(eval $(call app_rule,$(app)))) all: $(TARGETS)

这里有一个极其关键的顺序问题:all: $(TARGETS)这一行必须放在foreach之后。原因在于,make在解析all:规则时,会立刻展开$(TARGETS)来得到依赖列表(除非开了.SECONDEXPANSION)。如果all写在foreach前面,那一刻TARGETS还是空的,等于定义了all:没有任何依赖。这跟"变量要先用后算"是两回事,纯粹是解析顺序导致的。很多初学者在这里卡住,以为是eval没生效,其实是规则位置错了。

clean规则也可以顺手补上。既然所有构建产物都集中在build、obj、bin几个目录里,清理就很省事:

clean: rm -rf build obj bin

整个构建脚本现在只剩三部分:模块清单、模板定义、foreach生成区。手工Makefile里那种"上百行规则海洋"彻底消失了。

4. 让生成的规则互相引用:元数据收集与自动变量细节

4.1 eval不仅能生成规则,还能生成声明和赋值

如果你以为eval只能生成目标: 依赖这种规则行,那就浪费了一半功力。eval生成的文本,可以是任何makefile合法内容:变量赋值、伪目标声明、include指令,甚至另一个$(eval)的调用文本。

比如给每个模块生成一个自检入口:

define app_rule .PHONY: run_$(1) run_$(1): bin/$(1) ./bin/$(1) --self-check TARGETS += bin/$(1) endef

模板里.PHONY: run_$(1)这一行,经过eval解析后会变成真实的伪目标声明,跟手写的.PHONY效果一致。你也可以在模板里给某个变量做累加,把"生成规则"和"收集规则信息"合在一次eval里完成。这就是元数据收集:规则本身进了依赖图,规则的"名字"也进了变量,供后续统一处理。

回到4.1开头那句"包括另一个eval的调用文本"——技术上嵌套eval可用,但我并不推荐。嵌套会让转义层数指数级上升,排查问题时会极其痛苦。如果你发现自己需要在一个模板里再套一层eval,先停下来想想,是不是可以用两个foreach、两个模板分阶段完成。

4.2 自动变量转义对照表与模式规则取舍

前面提过自动变量在模板里的转义问题,这里给一份完整对照表,方便你写模板时直接查:

自动变量含义eval模板里的写法
$@目标名$$@
$<第一个前置依赖$$<
$^所有前置依赖(去重)$$^
$?比目标新的前置依赖$$?
$*模式规则中匹配的部分$$*
$(@D)目标所在目录$$(@D)
$(@F)目标文件名部分$$(@F)
$(<D)第一个前置依赖的目录$$(<D)
$(<F)第一个前置依赖的文件名$$(<F)

这里要提醒一句:eval不是万能的,也不是所有规则都该用eval生成。如果你的目标名有严格的字符串规律,比如所有源文件都走obj/%.o: src/%.c,直接写一条模式规则就够了,一行解决,根本不需要模板。eval真正不可替代的场景是:目标之间是离散的,每个目标还带有独立参数。比如按模块生成不同名字的二进制、按配置生成不同后缀的打包产物,这种"同构但不满足模式匹配"的情况,才是eval的主场。

4.3 多模块交叉依赖:生成规则之间如何协调

实际项目里模块之间很少完全独立,常有"A模块链接时依赖B模块生成的库"这种情况。eval生成规则会不会导致依赖顺序出问题?不会。读入期结束时,make已经把所有生成的规则都收进依赖图了,图的解析发生在执行期。所以在文件里,bin模板写在lib模板前面、还是lib模板写在bin模板前面,对依赖图的最终形态没有影响。

举例:

define lib_rule lib/$(1).a: obj/$(1).o $$(AR) rcs $$@ $$^ endef define bin_rule bin/$(1): lib/libcore.a $$(CC) $$(LDFLAGS) $$^ -o $$@ endef

即使bin_rule的foreach写在lib_rule前面,make依然能正确解析出bin/demo依赖lib/libcore.a。真正需要注意的是命名纪律:如果两个不同模板生成了同一个目标名,make会报"target given more than once"之类错误。我的习惯是所有生成目标都用$(1)做前缀或子目录隔离,从源头杜绝目标名冲突。

5. 我在真实构建里踩过的坑与调试脚手架

5.1 规则静默消失:展开顺序的典型错误

eval最坑的地方在于,出错了不一定报错,而是"静默生成空规则"。我踩过最典型的几个:

  • 模板里写$(CFLAGS),但CFLAGS在foreach之后才定义,eval展开时取到空值,生成出来的recipe变成一条没有编译参数的命令,编译却照样成功,只是产物不对。修法是模板里统一写$$(CFLAGS),让取值延迟到执行期。
  • all: $(TARGETS)写在foreach之前,依赖列表展开为空,make只输出"Nothing to be done"。这个前面说过,位置决定生死。
  • 试图在recipe里调用$(eval ...),想让make在每次构建时动态生成新规则。这是认知错误:recipe是执行期跑的,读入期早已结束,eval不会生效。如果你确实需要"每次构建时重新生成规则",正确做法是用$(file ...)把生成的makefile片段写到磁盘,再用include动态包含它。

第二条的排查经验是:遇到"目标没生成"或"命令行为不对",先怀疑展开顺序,再看转义;顺序问题的特征是有据可循(比如all放在前面),转义问题的特征是生成文本里多了空参数。

5.2 空格缩进与missing separator

这是eval用户最容易在复制代码时踩的雷:make要求recipe行以Tab开头,这个要求对eval生成的文本同样生效。如果你的编辑器默认把Tab转成四个空格,define块里的recipe行就会变成空格缩进,eval解析时报错信息通常是"missing separator"——它不知道这一行该当成规则、变量还是recipe。

更隐蔽的情况是,模板里有recipe行用了空格缩进,但前面恰好有一行规则定义,make会把recipe行解析成"游离的杂散文本",有时不报错,只是那条命令永远不执行。这种"不报错的错"最费时间。我的建议是:在编辑器里关掉"Tab转空格"功能,或者在define块写完后,用sed -n l这种命令检查一下Tab字符是否真的存在。

5.3 $(shell)与$(warning)在eval里的特殊行为

eval参数里如果出现$(shell ...),它会在每次make解析时真的执行一次shell命令。这意味着:

  • 有副作用的shell命令(写文件、改系统状态)放进eval里,等于每次构建都重复触发副作用,而且可能因为并行、失败重试等原因产生诡异行为。
  • 纯查询类命令(读版本号、获取当前时间)可以用,但也要意识到它拖慢了读入阶段。

比$(shell)更适合调试eval的是$(warning)和$(info)。这两个函数在展开时打印消息、返回空字符串,用法很直观。在模板里插一行$(warning now generating rule for $(1)),能在foreach循环时看到每次调用的参数;如果放在call之前或之后,还能观察文本生成的位置。

我在模板里调试时最喜欢的一句是:

define app_rule $(info [template] target=$(1)) ... endef

这样每次$(call)展开模板,都会打印一次当前模块名,一眼就能确认循环有没有跑、参数传对没有。

5.4 调试脚手架:先info打印,再eval,再make -p

最后给一套我屡试不爽的调试流程。

第一步,把eval临时改成info,只看生成的文本,不解析:

# 调试时先只打印,不解析 $(foreach app,$(APPS),$(info ===>> $(call app_rule,$(app)))) # 确认文本没问题再换成: $(foreach app,$(APPS),$(eval $(call app_rule,$(app))))

这一步能区分"模板生成错了"和"规则解析错了"。生成文本如果不对,后面的eval报什么错都先别管,先把模板和转义理顺。

第二步,用make -pR导出make的完整数据库。-p会把所有读入的规则打印出来,-R禁用内置规则减少噪音。在输出里grep你的目标名,比如make -pR | grep -A5 'build/alpha',就能看到eval生成的规则是否真的进了依赖图,依赖写没写对、recipe是什么,全都清清楚楚。

第三步,make -n做试运行,只看生成的shell命令不执行。这条很关键,因为规则进没进图是一回事,recipe展开成什么样子是另一回事。make -n会让你直观地看到$$(CFLAGS)到底展开成了什么,如果展开结果里有空参数或者$(残留,那基本可以断定是转义层数出了问题。

还有一点心理准备:eval相关的报错信息经常指向makefile:0或者一个不存在的行号,因为出错的内容来自运行时生成的文本,make无法回溯到模板的原始位置。所以功能越复杂的eval模板,越要保住命名纪律和区域注释,否则半年后你自己也看不懂那段生成代码。我现在的构建脚本里,规则生成区占了将近一半,新增模块只需要在模块清单里加一行,剩下的事全部交给eval。如果让我给一个最重要的习惯,那就是:模板文件名统一加前缀清晰标识,foreach生成区集中放在文件底部,并配上大段注释说明"这个区域只允许改模块清单和模板,不要在这里手写规则"——这样团队里其他人接手时,才不会把eval生成的东西误当成普通规则去改。

相关新闻

开源CLI代码审查工具:深度集成Git与本地LLM的自动化评审工作流

开源CLI代码审查工具:深度集成Git与本地LLM的自动化评审工作流

1. 项目概述&#xff1a;这不是又一个“AI写代码”玩具&#xff0c;而是一套可嵌入日常开发流的开源代码审查工作流“open-code-review”这个名字乍看平平无奇&#xff0c;但拆开来看——open&#xff08;开源&#xff09;、code&#xff08;代码&#xff09;、review&#xff…

2026/10/19 19:12:08 阅读更多 →
用BoxBlur实现Android玻璃蒙层:兼容API21的自定义View完整指南

用BoxBlur实现Android玻璃蒙层:兼容API21的自定义View完整指南

简介&#xff1a;这是一份面向安卓开发者的玻璃蒙层实现工程&#xff0c;聚焦于半透明蒙层、高斯模糊及毛玻璃效果&#xff0c;适用于需要增强界面层次感与交互反馈的场景&#xff0c;技能上覆盖从初级到中级的界面特效实现。压缩包共55个文件&#xff0c;仅1.23MB&#xff0c;…

2026/10/19 19:11:06 阅读更多 →
CS144 lab0从零构建TCP协议栈:webget实现与字节级思维训练

CS144 lab0从零构建TCP协议栈:webget实现与字节级思维训练

1. 从零理解CS144 lab0到底在练什么很多人第一次打开CS144的lab0文档&#xff0c;看到"Networking Warmup"这个标题&#xff0c;第一反应是"热身而已&#xff0c;应该很简单"。我当初也是这么想的&#xff0c;结果花了整整一个下午才把第一部分跑通。后来回…

2026/10/19 19:10:04 阅读更多 →

最新新闻

2026 最新抖音 SEO 推广全攻略,吃透抖音 SEO 视频排名逻辑

2026 最新抖音 SEO 推广全攻略,吃透抖音 SEO 视频排名逻辑

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

2026/10/19 20:50:01 阅读更多 →
菲涅尔衍射模型解析WiFi呼吸检测:从原理到工程实践

菲涅尔衍射模型解析WiFi呼吸检测:从原理到工程实践

1. 从光学衍射到无线感知&#xff1a;这个项目到底在做什么 菲涅尔衍射和WiFi呼吸检测&#xff0c;乍一看是两个八竿子打不着的领域。一个是光学课本里的经典波动光学内容&#xff0c;一个是近几年火起来的无线感知应用。但把这两个东西放在一起&#xff0c;恰恰说明了一个很有…

2026/10/19 20:48:59 阅读更多 →
西门子1200与FactoryIO机械手仿真:状态机实现虚拟调试全流程

西门子1200与FactoryIO机械手仿真:状态机实现虚拟调试全流程

做工业自动化调试&#xff0c;最怕设备还没到场、程序就得交付&#xff1b;最怕机械没接线、电气没上电&#xff0c;写好的逻辑只能在脑子里跑。我前阵子捣鼓的一套FactoryIO机械手仿真取料程序&#xff0c;算是把这个困局撕开了一个口子&#xff1a;FactoryIO提供虚拟车间&…

2026/10/19 20:48:59 阅读更多 →
图表设计实战指南:从认知到维护的完整方法论

图表设计实战指南:从认知到维护的完整方法论

1. 从"diagram-design"这个标题能读出什么 第一次看到"diagram-design"这个标题&#xff0c;我的直觉是&#xff1a;这大概率不是一个单纯的"画图工具推荐"或者"设计规范文档"&#xff0c;而是一个把 图表设计 这件事当成一个完整工…

2026/10/19 20:46:54 阅读更多 →
图表设计方法论:从沟通逻辑到视觉规范的完整实践指南

图表设计方法论:从沟通逻辑到视觉规范的完整实践指南

1. 图表设计&#xff1a;从“画出来”到“讲清楚”这些年我看了不少技术文档、项目方案和系统说明&#xff0c;发现一个共性问题&#xff1a;大部分人根本不把图表当“设计”来做&#xff0c;而是当“画图”来应付。打开绘图工具&#xff0c;拖几个方框&#xff0c;连几条线&am…

2026/10/19 20:43:51 阅读更多 →
Python+Vue游戏装备交易商城系统:架构设计与实战全解析

Python+Vue游戏装备交易商城系统:架构设计与实战全解析

“基于PythonVue的游戏装备交易商城系统”这个标题&#xff0c;我太熟了。这几乎是每一届想做点东西但又不想只写个管理系统的同学都会碰到的题&#xff0c;也是后台私信里被问得最多的项目类型之一。但说句实话&#xff0c;真正把“交易”两个字做明白的&#xff0c;十个里可能…

2026/10/19 20:42:50 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介&#xff1a;本资源为电源技术方向的学术研究文档&#xff0c;聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略&#xff0c;面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 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/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址&#xff1a; https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集&#xff0c;共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →