news 2026/10/3 8:39:04

learngo 复古 LED 时钟项目四道数组实战练习:重构、闹钟、百分秒与滚动动画

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
learngo 复古 LED 时钟项目四道数组实战练习:重构、闹钟、百分秒与滚动动画
  • 示例工程
  • 教程

【免费下载链接】learngo

❤️ 1000+ Hand-Crafted Go Examples, Exercises, and Quizzes. 🚀 Learn Go by fixing 1000+ tiny programs.

项目地址:https://gitcode.com/gh_mirrors/le/learngo
点击查看免费下载

导读

本指南围绕learngo仓库中 15-project-retro-led-clock/exercises/README.md 展开,逐一拆解 Retro LED Clock 项目的四道进阶练习:多文件重构(Refactor)、每隔 10 秒显示闹钟(Set an Alarm)、显示十分之一秒的百分秒(Split Second),以及从右向左滑动的时钟动画(Ticker)。完成本指南后,你将掌握数组([...]T与[5]string)的声明、赋值、替换与索引运算在真实终端动画程序中的完整用法,并理解 Go 的包级作用域、短声明限制与多文件同包编译规则。

项目背景:Retro LED Clock 五步曲

Retro LED Clock 是learngo课程中用于强化"数组"知识的大型综合项目,项目主目录 15-project-retro-led-clock/README.md 将整个项目划分为五个循序渐进的目标步骤:

  1. 打印数字(01-printing-the-digits):定义占位符类型,为 0~9 每个数字创建[5]string数组,放入digits数组并排输出;
  2. 打印时钟(02-printing-the-clock):获取当前时间,构造时钟数组,加入冒号分隔符;
  3. 动画时钟(03-animating-the-clock):无限循环每秒刷新、清屏、将光标移到左上角;
  4. 冒号闪烁(04-blinking-the-separators):让分隔符按秒奇偶闪烁;
  5. 完整带注释解答(05-full-annotated-solution/main.go)。

而 exercises/README.md 正是在前四步成果之上的四道强化练习,目的是"证明你能写出易于维护的 Go 程序"。

练习一:Refactor —— 把占位符搬进独立文件

目标:将时钟项目的一部分代码移动到同包下的新文件中,使项目更易长期维护。具体分四步:

  1. 新建文件placeholders.go;
  2. 把placeholder类型移到placeholders.go;
  3. 把 0~9 及冒号的所有占位符移到placeholders.go;
  4. 把digits数组也移到placeholders.go。

练习文件位于 01-refactor/main.go,其顶部注释给出了三条关键提示:

  • placeholders.go必须同样属于main包(重温"PART I — Packages, Scopes and Importing"课程);
  • 包作用域下短声明(:=)不可用,必须改用var(这是"Short Declaration: Package Scope"课程的核心结论);
  • 若收到undefined: placeholder/undefined: colon的编译错误,说明你没有同时编译多个文件,需要用go run .方式运行同包多文件。

重构后的 01-refactor/solution/placeholders.go 展示了答案的核心模式:

package main type placeholder [5]string var zero = placeholder{ "███", "█ █", "█ █", "█ █", "███", } // ... one 到 nine 同理 ... var colon = placeholder{ " ", " ░ ", " ", " ░ ", " ", } var digits = [...]placeholder{ zero, one, two, three, four, five, six, seven, eight, nine, }

而 01-refactor/solution/main.go 只剩主循环逻辑:screen.Clear()清屏 →screen.MoveTopLeft()复位光标 → 取time.Now()的时/分/秒 → 用digits[hour/10]、digits[hour%10]等索引运算组装clock数组 → 逐行逐列打印,time.Sleep(time.Second)控制每秒刷新。

期望输出:重构前后运行结果完全一致。这一练习的价值在于验证你对"同一目录下多个.go文件同属一个包"和"包级变量必须用var声明"两个知识点的掌握。

练习二:Set an Alarm —— 每 10 秒打印 ALARM

目标:在时钟运行过程中,每隔 10 秒将画面切换为" ALARM! "字样。练习文件为 02-alarm/main.go 与 02-alarm/placeholders.go。

注释中给出提示:可以新建一个与clock数组等长的alarm数组,每 10 秒把alarm复制到clock数组。alarm数组由 8 个placeholder组成,其中首尾两个是空白占位符(用于对齐),中间 6 个分别对应 "ALARM" 六个字母的 LED 字形,见 02-alarm/solution/placeholders.go:

var alarm = [...]placeholder{ {" ", " ", " ", " ", " "}, // 空白对齐 {"███", "█ █", "███", "█ █", "█ █"}, // A {"█ ", "█ ", "█ ", "█ ", "███"}, // L {"███", "█ █", "███", "█ █", "█ █"}, // A {"██ ", "█ █", "██ ", "█ █", "█ █"}, // R {"█ █", "███", "█ █", "█ █", "█ █"}, // M {" █ ", " █ ", " █ ", " ", " █ "}, // ! {" ", " ", " ", " ", " "}, // 空白对齐 }

核心判据是 02-alarm/solution/main.go 中的一行:

alarmed := sec%10 == 0

当秒数能被 10 整除(即每 10 秒一次)时,在逐行打印之前执行clock = alarm,用alarm数组整体替换clock数组——这正是数组可以整体赋值、比较语义的直接体现:

for line := range clock[0] { if alarmed { clock = alarm } // ...打印逻辑不变... }

期望输出中," ALARM! " 字形还带有░冒号闪烁效果,与前四步的sec%2 == 0逻辑保持一致。

练习三:Split Second —— 时钟加速到十分之一秒

目标:把刷新粒度从每秒提升到每 1/10 秒(100 毫秒),并在时钟右侧追加一个"百分秒"数字。练习文件为 03-split-second/main.go,注释给出了详细步骤:

  1. 计算当前的百分秒(split second);
  2. 在时钟中加入点字符(dot);
  3. 把百分秒数字加入时钟数组;
  4. 让点字符像冒号一样每两秒闪烁;
  5. 把刷新周期从 1 秒改为 1/10 秒。

注释提示:使用Time类型的Nanosecond方法,百分秒就是Nanosecond的第一位数字;并且time.Second是整数常量,可以直接除以数字。

03-split-second/solution/main.go 给出了完整答案:

now := time.Now() hour, min, sec := now.Hour(), now.Minute(), now.Second() ssec := now.Nanosecond() / 1e8 clock := [...]placeholder{ digits[hour/10], digits[hour%10], colon, digits[min/10], digits[min%10], colon, digits[sec/10], digits[sec%10], dot, digits[ssec], }

关键点:

  • now.Nanosecond() / 1e8取出纳秒值的第一位数字(0~9),即为百分秒;
  • clock数组扩为 10 个元素,末尾追加dot和百分秒数字;
  • 闪烁条件扩展为if (digit == colon || digit == dot) && sec%2 == 0,点与冒号同节奏闪烁;
  • 刷新周期改为const splitSecond = time.Second / 10; time.Sleep(splitSecond),印证了time.Second整数常量可参与除法运算的特性。

期望输出展示了多种百分秒与秒的组合形态,注意当时钟以十分之一秒刷新时,画面会以 10 倍于原版的速度动态更新。

练习四:Ticker(高难度)—— 从右向左滑动时钟

目标:让整个时钟动画从屏幕右侧向左侧连续滑动。这是四道练习中标注HARD EXERCISE的一道,练习文件为 04-ticker/main.go。

04-ticker/solution/main.go 的核心思路是引入一个无限递增的shift变量,用取模运算控制起始索引,同时利用"把时钟长度想象成两倍"的技巧实现回绕:

for shift := 0; ; shift++ { screen.Clear() screen.MoveTopLeft() now := time.Now() hour, min, sec := now.Hour(), now.Minute(), now.Second() clock := [...]placeholder{ digits[hour/10], digits[hour%10], colon, digits[min/10], digits[min%10], colon, digits[sec/10], digits[sec%10], } for line := range clock[0] { l := len(clock) // 用 shift%l 防止越界 s, e := shift%l, l // 当 shift%(l*2) >= l 时,说明时钟已完全滑出屏幕左端, // 重置起始位置为 0,结束位置为 s,形成回绕 if shift%(l*2) >= l { s, e = 0, s } // 打印空白占位,营造从右向左移动的效果 for j := 0; j < l-e; j++ { fmt.Print(" ") } // 从 s 到 e 绘制数字 for i := s; i < e; i++ { next := clock[i][line] if clock[i] == colon && sec%2 == 0 { next = " " } fmt.Print(next, " ") } fmt.Println() } time.Sleep(time.Second) }

值得注意的工程细节(源码注释中均有说明):

  • 每次循环都调用screen.Clear(),否则上一帧的字符会残留在屏幕上;另一种替代方案是用空白行填满缺失的占位符;
  • shift%l防止索引越界;shift%(l*2) >= l判定时钟是否已整体滑出左端,从而把(s, e)从(shift%l, l)重置为(0, s);
  • l-e仅在上述if分支生效时才非零,此时前置空白行让"移出屏幕的占位符位置"由空位补齐,正是把时钟长度虚拟为两倍的收益所在。

完成这道练习后,for range遍历、%取模、数组整体比较与索引切片式的打印逻辑就全部串联起来了,这也是文档中所说的"你将真正感到自己掌握了此前学到的所有内容"。

如何运行与验证

每个练习目录都采用"题目 +solution/答案"的双目录结构,例如01-refactor/main.go与01-refactor/solution/main.go。由于重构后涉及同包多文件,运行方式为:

cd 15-project-retro-led-clock/exercises/01-refactor go run . # 或 go run *.go

其中go run .会编译当前目录下同一包的所有.go文件,这正是练习一注释中针对undefined: placeholder错误给出的解决方案。程序运行依赖github.com/inancgumus/screen包(见仓库根目录 go.mod),用于终端清屏与光标定位,因此请在支持 ANSI 转义的终端中运行以获得最佳显示效果。

各练习的期望输出都写在对应main.go顶部的注释块中(如闹钟的 " ALARM! " 五层字形、百分秒时钟的多种组合形态),你可以据此逐帧比对验证自己的实现是否正确。

小结

这四道练习环环相扣:Refactor 强调代码组织与包级作用域;Alarm 展示数组整体赋值替换;Split Second 把time.Nanosecond、整数常量除法与数组扩围结合;Ticker 则以取模回绕算法完成高难度的滑动动画。它们共同覆盖了learngo课程中数组章节的全部核心知识点,适合在学习完 14-arrays 后通过"动手改错"的方式巩固掌握。若某一步卡住,项目主 README 还提供了分步解答(05-full-annotated-solution/main.go),可以按步骤对照而不必一次看完整答案。

  • 示例工程
  • 教程

【免费下载链接】learngo

❤️ 1000+ Hand-Crafted Go Examples, Exercises, and Quizzes. 🚀 Learn Go by fixing 1000+ tiny programs.

项目地址:https://gitcode.com/gh_mirrors/le/learngo
点击查看免费下载

相关推荐

上一篇:OpenZeppelin Contracts 的 ERC-4337 担保式 Paymaster 修复:有效预存款(Effective Prefund)如何在扩展组合中正确传播
下一篇:Dashy + Tailscale 私有网络部署指南:借助 Tailscale Serve 与 Header Auth 实现免密自动登录

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 8:37:59

pikachu暴力破解

漏洞描述暴力破解漏洞是指系统在登录、验证码、支付密码或 API 认证等环节缺乏有效的失败次数限制、验证码、账户锁定、多因素认证和异常检测&#xff0c;导致攻击者可利用自动化工具反复尝试用户名、密码、短信验证码或密钥组合&#xff0c;从而猜出有效凭证并实现账户接管、数…

作者头像 李华
网站建设 2026/10/3 8:32:00

LRCTI: A Large Language Model-Based Framework for Multi-Step Evidence Retrieval and Reasoning in ...

文章主要内容和创新点 主要内容 本文提出了一种基于大语言模型(LLM)的框架LRCTI,用于网络威胁情报(CTI)的多步证据检索与可信度验证。传统CTI可信度验证方法存在局限性(如静态分类、依赖手工特征、透明度低等),而LRCTI通过三个核心模块解决这些问题: 文本摘要模块:…

作者头像 李华
网站建设 2026/10/3 8:31:31

猫抓:三步搞定网页视频下载的浏览器资源嗅探扩展

猫抓&#xff1a;三步搞定网页视频下载的浏览器资源嗅探扩展 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 猫抓&#xff08;cat-catch&#xff0…

作者头像 李华
网站建设 2026/10/3 8:22:33

浏览器中跑3步部署WebVM:WebAssembly Linux沙箱完整上手指南

浏览器中跑3步部署WebVM&#xff1a;WebAssembly Linux沙箱完整上手指南 【免费下载链接】webvm Virtual Machine for the Web 项目地址: https://gitcode.com/GitHub_Trending/we/webvm 想在网页里验证几条 Linux 命令、跑一跑别人的示例脚本&#xff0c;第一反应却是下…

作者头像 李华
网站建设 2026/10/3 8:21:42

ZeroTermux 内置 bzgrep 命令详解:在 .bz2 压缩包内直接进行正则搜索

移动开发开发工具 【免费下载链接】ZeroTermux 项目地址&#xff1a; https://gitcode.com/GitHub_Trending/ze/ZeroTermux 点击查看 免费下载 bzgrep 是 ZeroTermux 内置 Linux 命令手册中收录的一条核心压缩检索命令&#xff1a;它允许你不用先解压&#xff0c;直接在 .bz2 …

作者头像 李华