我今年 完成 了 Advent of Code!除了问题比去年(对我来说)变得简单了之外,我换用 Go 完成了今年的挑战:我觉得 Go 特别适合这类任务。
今年的谜题主要涉及字符串解析以及寻找最高效的数据结构。大部分解题流程的逻辑都比较简单,几乎不需要任何复杂的算法。
对于字符串解析,正则表达式(Go 自带支持)是最佳途径。今年为数不少的字符串解析问题意味着想只用基本的字符串操作解答会十分痛苦。我已经看够了用查找、剪切、提取子串等操作拼砌起来的怪物代码。
绝大部分时候,切片(slice)和集合(map)足够满足我的要求。Go 支持多返回值,却不支持元组(tuple);不过,我发现序列(array)和结构体(struct)基本取代了元组的功能。这些数据结构由于 Go 鼓励使用常量而非枚举类型(enum)而变得更加多功能:将所有信息储存为整形(int)使得一些捷径成为可能,同时也省去了类型转换的麻烦。和更让人安心的支持类型检查的枚举类型不同,(滥用)这些选择让用 Go 写短程序有着和行走在刀刃上(或者在 Zoom 通话时不穿裤子)一样的莫名快感。
Go 简单明了的控制流和缺少多范式支持的特点让指令式程序编写起来非常容易。不必担心是否应该使用 STL 算法或者串联迭代器方法:写个循环就好了。合理的对象可变机制也有帮助:不论是边遍历边修改集合元素,还是将带有切片的结构体传给函数,我发现我可以很快地让 Go 做我所想的事情,而不用反复翻阅语言规范。
这让我想到了 ZOI 这一经验法则:只有零、一、无穷多是合理的个数。不少我所熟悉的编程语言,例如 Python、C++、Rust,都为了追求一致性落在这一光谱的两极:一切都有着相同的规则,用户就和语言本身一样有着决定语法含义的能力。Go 有着更多的异常之处(尽管 Go 并没有异常支持)和只有“一”个的例外:内置容器神奇地有泛型支持、它们方法的返回值数量可变、除它们之外的容器都没有获得迭代支持的资格。
这只是一个不涉及 Go 其他特性(例如界面(interface)或协程(goroutine),不过这些对解决 Advent of Code 并不是必须的)的简单比较。我觉得 Go 的选择独特且有趣:一切都,嗯,不大一样。