「速记」拒绝迷信 间隔复习! 我给自己的闪卡系统添加了 假期 功能
背景
随着闪卡系统素材量的进一步增加, 每天的复习时间越来越多. 而且因为只有十分重要和有价值的素材, 我才会选择使用闪卡系统.这就导致我每次复习都必须高度集中注意力.
长时间集中注意力, 一个显而易见的后果就是: 疲劳! 对, 就是疲劳! 而过度的疲劳, 随之而来的就是大脑的抗拒和抵触.
我不是 奋斗型 人格, 我也不会尝试去以 “自律” 的名义 来强迫自己的大脑去做事. – 尽管, 我曾经这么做过. 我现在更倾向于: 我要试着尊重自己的感觉. 我要接纳大脑的疲倦. 原因也非常简单. 跑的快的人, 不一定跑的远.因为方向不对, 跑的越来, 反倒会偏离终点更远. 而和大脑和谐共处, 尊重大脑的感受, 方能不时地审视自己的处境, 寻找更好的方向,更好的方法. 简言之: 我认为人应该和自己的大脑和解.尊重和接纳自己.如此, 跑的慢, 未必就走得不比别人远.
现在闪卡系统占用的时间已经到了 小时 级别. 我不得不重视. 我发现我无法说服大脑, 在本该放松的周末, 来去复习这些闪卡.我尊重它的选择. 我也十分认同, 周末就应该好好放松.玩玩 LOL 的无限活力, 真的很让人放松. 玩的过程中, 其实也真的会思考一些事情. 比如: 我在做的事, 是不是真的有必要? 有没有更好的选择? 远离家人, 待在异国他乡, 是不是一个最优的选择? 如果必须投入巨量资源解决语言问题, 是不是欧洲国家,更合适一些? 其实是有一些很认真的思考和权衡的. 如果不久的将来, 我选择离开日本, 我也不觉得是我的问题. 对, 是这个国家的损失. 它失去的不是我, 而是我这一类人, 我这一类更注重精神感受的人. 当然, 未来的事, 未来再说吧. 最重要的是, 要学会尊重自己的大脑. 让大脑适时处于略松弛的状态, 让它帮我们好好想想, 除了眼下的事, 还有没有其他的可能.
单纯考虑时间, 在一定的假期之后, 尝试恢复闪卡复习节奏, 也是很困难的. 这次是3天假期. 如果我选择完成所有闪卡的复习, 就意味着我周一几乎啥都不用干了. 显而易见, 这是不可能的事.
另一个层面, 所谓的 “间隔复习” 的周期, 并没有那么神奇. 间隔复习, 是有必要的. 但是到底是否遗忘, 在你复习之前, 你可能是无法知晓的. 这是一个悖论. 我想表达的是: 让自己好好休息. 如果遗忘了, 卡片会自己回到前一周期, 后续会额外多复习; 没有遗忘的, 当然更好.
在写这些东西之前, 我还专门检索了下. 目前还没有看到能够简单直观地支持 “假期” 的闪卡系统. 彷佛: 每日复习, 是一个不容触碰的 间隔复习的底线. — 这很搞笑. 就好像说: 我必须每天编码, 才能保持较高的编码水平一样, 一样扯淡!
基础需求:
- 周六日, 自动设置为假期.
- 其他法定假期, 能够支持手动添加和配置.
- 计算卡片复习时间时, 能够自动跳过 假期.
设计难点:
-
如何跳过假期? – 说实话, 并没有太好的方法.真的就是遍历, 每天判定.
-
如何高性能运转. – 这是一个可以预期的问题. 每次用到卡片时, 都独立运算一次, 性能上肯定达不到要求. 当然, 稳妥起见, 初版还是直接运算的. 切换卡组时, 也是毫不意外的, UI卡住了.
核心代码:
基础思路, 非常简单. 就是简单的在计算复习时间,跳过那些节假日就行了. 所以, 我只贴一些核心的修改.
卡片状态, 增加一个 holiday 的配置:
下面的例子, 就是把 2024-02-2 和 2024-02-26 设置为了假期. holiday 用的是 hashMap, 而不是数组,当然也是为了性能考虑的小心思了. 可以预见, holiday 肯定会越来越多的.
"config": {
"stage": {
"1": 3,
"2": 5,
"3": 8,
"4": 13,
"5": 21,
"6": null
},
"holiday": {
"2024-02-23": true,
"2024-02-26": true
}
},
每个卡片新增两个字段: reviewDate 和 reviewDateHash:
-
reviewDate: 下次复习的时间. 这个时间就是下次应该复习该卡片的时间. 计算这个时间时, 已经考虑了假期. 类似于一个缓存机制吧. 这样就不用每次动态计算卡片的 复习时间 了.
-
reviewDateHash: 是由当时计算 reviewDate 时用到的一些核心信息,组成的字符串. 因为缓存的 reviewDate,总是要在合适的时机,去缓存的.
-
卡片reviewDateHash目前的策略如下:
// 用于辅助支持自定义假期. 周六日会被直接判定为假期.
const baseHolidayHash = hashCode(JSON.stringify(baseState.config['holiday']));
const cardReviewDateHash = `${baseHolidayHash}_${card.stage}_${intervalGap}_${card.lastStudy}`;
这样做可以保证,在下列场景下, 都会重新计算 复习时间:
- 假期有变更.如: 新增了新的假期.
- 复习间隔的配置有变动.
- 卡片本身的状态有变动.比如: 卡片OK导致卡片 stage 来到了下一级.
计算复习时间时, 跳过假期和周六日:
也没啥神秘的.真的就是从上次复习的日期, 逐日遍历:
while (days > 0) {
result.setDate(result.getDate() + 1);
if (!isWeekend(result) && !isHoliday(result)) {
days--;
}
}
isHoliday 和 isWeekend 的实现:
代码本身, 没啥难度. 网上也能搜到很多类似的例子. 但是有一个细节, 为了便于人手动配置, 需要将日期的特定格式来对比.为了避免歧义, 我统一用两位数字来表示 月和日,如: 2024-02-23.
function isHoliday(date) {
const day = date.getDate();
const month = date.getMonth() + 1;
const year = date.getFullYear();
// ref: https://stackoverflow.com/a/2998874
const zeroPad = (num, places) => String(num).padStart(places, '0');
const dateStr = `${year}-${zeroPad(month, 2)}-${zeroPad(day, 2)}`;
return true == baseState.config['holiday'][dateStr];
}
function isWeekend(date) {
const Sunday = 0;
const Saturday = 6;
const dayOfWeek = date.getDay()
return dayOfWeek == Sunday || dayOfWeek == Saturday;
}
小感: 越来越感觉, 果断弃用第三方闪卡系统, 是一个很明智的决定.
当时自制闪卡系统的前因后果, 可以参考: 我为什么暂停一周,做了一个Anki替代品?
回头来看. 间隔复习本身的价值, 我是十分推崇的.但是已有的闪卡系统, 说实话, 不敢恭维.
他们, 可能比我更懂记忆原理, , 但是他们可能没有我更懂生活.