@@ -4,8 +4,9 @@ author: xihale
44date : 2025-01-23T12:00:00+08:00
55---
66
7- > 原文: < https://www.scottredig.com/blog/bonkers_comptime/ >
8- > 译注:原文中的代码快是交互式,翻译时并没有移植这一块。另外,由于 comptime 本身即是关键概念,并且下文的意思更侧重于 Zig comptime 的特性,故下文大多使用 comptime 代替(涵盖)编译时概念。
7+ > 原文: < https://www.scottredig.com/blog/bonkers_comptime/ >
8+
9+ > 译注:原文中的代码快是交互式,翻译时并没有移植这一块。另外,由于 comptime 本身即是关键概念,并且下文的意思更侧重于 Zig comptime 的特性,故下文大多使用 comptime 代替编译时概念。
910
1011## 引子
1112
@@ -17,7 +18,7 @@ date: 2025-01-23T12:00:00+08:00
1718
1819为了明确起见,所有示例都是有效的 Zig 代码,但示例中的转换只是概念性的,它们并不是 Zig 实际的实现方式。
1920
20- ## 视角1 : 忽略它
21+ ## 视角0 : 忽略它
2122
2223我说我喜欢这个特性,却又立刻叫你忽略它,这确实有点怪。但我认为此处正是 Zig comptime 威力所体现的地方,所以我将从这里出发。Zig Zen 中的第三条是“倾向于阅读代码,而不是编写代码。”确实,能够轻松地阅读代码在各种情况下都很重要,因为它是建立概念理解的基础,而这种理解也是调试或修改代码所必需的。
2324
@@ -146,7 +147,7 @@ pub fn main() !void {
146147}
147148```
148149
149- 确实很简单。但是,每当讨论如何优化 Fizz Buzz 算法时,人们总是忽略一个事实:标准的 Fizz Buzz 问题只需要输出前100个数字的结果。既然输出是固定的,那为什么不直接预先计算出答案,然后输出呢?(由此,我时常认为那些有关优化讨论有些滑稽的。)
150+ 确实很简单。但是,每当讨论如何优化 Fizz Buzz 算法时,人们总是忽略一个事实:标准的 Fizz Buzz 问题只需要输出前100个数字的结果。既然输出是固定的,那为什么不直接预先计算出答案,然后输出呢?(由此,我时常认为那些有关优化讨论有些滑稽的。)
150151我们可以使用相同的 Fizz Buzz 函数来实现这一点。
151152
152153``` Zig
@@ -225,7 +226,7 @@ onst MyStruct = struct {
225226 inline for (comptime std.meta.fieldNames(MyStruct)) |field_name| {
226227 sum += @field(my_struct, field_name);
227228 }
228-
229+
229230 // 这可以展开为:
230231 {
231232 const field_name = "a";
@@ -249,7 +250,7 @@ onst MyStruct = struct {
249250};
250251```
251252
252- 上面的示例是我们手动展开后的示例,但这项工作是由 Zig 的 comptime 完成的。这使得我们可以直接独立而完整地编写出我们要实现的功能,而不需要添加"当你改变 ` MyStruct ` 的字段时,记得更新 sum 函数"这样的由于依赖于 ` MyStruct ` 具体字段而预防功能失效的注释。
253+ 上面的示例是我们手动展开后的示例,但这项工作是由 Zig 的 comptime 完成的。这使得我们可以直接独立而完整地编写出我们要实现的功能,而不需要添加"当你改变 ` MyStruct ` 的字段时,记得更新 sum 函数"这样的由于依赖于 ` MyStruct ` 具体字段而预防功能失效的注释。
253254基于 comptime 的版本在 ` MyStruct ` 的任何字段变更时都可以正确地自动处理。
254255
255256## 视角4:Comptime 求值,运行时代码生成
@@ -310,8 +311,6 @@ const MyStruct = struct {
310311
311312下有两例。第一个是一个元编程的示例,第二个是我们熟悉的 comptime 示例。这两个版本的代码有着相同的逻辑。
312313
313- > 元编程
314-
315314``` Zig
316315pub fn writeSumFn(
317316 writer: std.io.AnyWriter,
@@ -328,7 +327,7 @@ pub fn writeSumFn(
328327}
329328```
330329
331- 注意这里有两个转换:
330+ 注意这里有两个转换:
3323311 . 在生成器中直接运行的代码是 comptime 的一部分
3333322 . 在生成器执行后输出的代码,成为运行时的一部分
334333
@@ -359,11 +358,11 @@ pub fn writeMyStructOfType(
359358}
360359```
361360
362- 以上 struct 字段的生成体现了上述两种转换( ` conversions ` )方式 ,并且将两者混合在了一行中。 字段的类型表达式由生成器/运行时完成,而字段本身则作为运行时代码使用的定义。
361+ 以上 struct 字段的生成体现了上述两种转换方式 ,并且将两者混合在了一行中。 字段的类型表达式由生成器/运行时完成,而字段本身则作为运行时代码使用的定义。
363362
364363在 comptime 下,引用类型名称的方式更加直接,可以直接使用函数,而不必将文本拼接成一个在代码生成中保持一致的名称。
365364
366- 这种观点有一个例外。 您可以创建字段名称在编译时就已确定的类型,但这样做需要调用一个内置函数,该函数包含一个字段定义列表。 因此,您无法在这些类型上定义方法等声明。 在实践中,这并不会限制代码的表达能力,但确实限制了你可以向其他代码公开哪些类型的 API。
365+ 这种观点有一个例外。 您可以创建字段名称在编译时就已确定的类型,但这样做需要调用一个内置函数,该函数包含一个字段定义列表。 因此,您无法在这些类型上定义方法等声明。 在实践中,这并不会限制代码的表达能力,但确实限制了你可以向其他代码公开哪些类型的 API。
367366
368367与本节相关的是文本宏,如 C 语言中的文本宏。你可以做的大多数正常事情都可以在 comptime 中完成,尽管它们很少采用类似的形式。 不过,文本宏并不能做所有允许做的事情。 例如,你不能决定不喜欢某个 Zig 关键字,然后让宏代替你自己的关键字。 我认为这是一个正确的决定,尽管对于那些习惯了这种能力的人来说,这是一个艰难的过渡。 此外,Zig 参考了半个世纪以来的程序员在这方面的探索,所以它的选择要理智得多。
369368
@@ -386,3 +385,5 @@ Zig 并非一个仅仅依赖 comptime 这一特性的语言。你可以在[官
386385
387386Zig 的函数可以具有几种不同的返回类型。但是,这并不是依赖于编译器中的某些魔法的操作,而只是[ 典型的 comptime 的应用] ( https://github.com/ziglang/zig/blob/0.13.0/lib/std/start.zig#L508 ) 。
388387
388+ > 如果您希望就本篇文章向我提出意见或更正,请发送电子邮件至 [email protected] 。389+ > 译者注:如果觉得翻译有问题,请提 PR 改正:< https://github.com/zigcc/zigcc.github.io >
0 commit comments