Skip to content

FIX:FSRS算法无限循环复习#75

Open
JYinherit wants to merge 2 commits intoOctagonalStar:mainfrom
JYinherit:main
Open

FIX:FSRS算法无限循环复习#75
JYinherit wants to merge 2 commits intoOctagonalStar:mainfrom
JYinherit:main

Conversation

@JYinherit
Copy link
Copy Markdown
Contributor

[Arabic Learning] 规律学习 (FSRS) 核心逻辑重构与体验优化

🐛 描述问题与复现步骤

1. FSRS 复习卡片“无限套娃”死循环

问题描述:
在 FSRS 规律复习模式下,系统在获取下一张复习卡片时,使用了 difference(DateTime.now()) < Duration(days: 1) 作为“是否到期”的判断标准。这意味着只要卡片的下一次排期在未来 24 小时内(例如首次复习答对后,系统安排于10分钟后或次日再次复习),都会被系统立刻判定为“当前到期”。这导致刚刚复习完的生词会被系统直接反查出来,造成用户陷于对同一单词的无限循环复习中。

复现步骤:

  1. 开启规律学习,刷出一个较生疏的单词。
  2. 作答完成后,FSRS 算法将其下一步学习计划定于 10 分钟或数小时后。
  3. 系统并没有等候该时间差,而是直接紧接着弹出这同一个单词。
  4. 直到用户多次作答,将其间隔推长至大于 24 小时,该词才会消失。

2. 无限滑动列表导致的内存与状态黑洞

问题描述:
原有的复习界面(MainFSRSPage)采用了无边际的 PageView.builder。当系统缺乏其他待复习单词时,只要用户不断下滑,视图构建器就会在原有的无限死循环上,持续发起对底层数据库乃至 getLeastDueCard() 的重复索求请求,既存在 OOM 风险,也让用户缺乏对“完整学习周期(一轮做完)”的掌控感。


🛠️ 修改方案与实现逻辑

🟢 修复与重构:抛弃模糊期限,引入严格基于时间戳的调度隔离

重写了 fsrs_func.dart 中判断词库是否到期的核心方法(getLeastDueCardgetWillDueCount)。
删除了原先 < 1天 的过度宽容时间差计算逻辑,全面替换为严格的时间线校验 isBefore(DateTime.now())。现在,FSRS 算法指定 10 分钟后复习的词,绝不会在 9 分 59 秒时出现,彻底保证了“间隔重复”记忆法(Spaced Repetition)在此环节的精准生效。

🟢 新功能/重构:阻断式有限队列渲染与强化记忆循环

废弃原有无限刷新的纯无状态组件,将 MainFSRSPage 升维重构为内置独立生命周期管线的 StatefulWidget

实现逻辑亮点:

  • 入场快照预加载: 用户进入或点击“刷新”进入学习页时,组件在 initState 阶段通过钩子函数一次性全量提取此刻所有满足 isBefore 条件的到期词汇,封存在不变内存列表内。杜绝了滑动过程中的重复查询与词汇变动。
  • 触底完结反馈: 由于构建了有限 PageView (长度定死为 待学字数 + 首尾状态页 2 页),当复习清单被打通后,用户下滑将明确看到“本轮复习完成”的终点提示页。结合顶部的 🔄 手动刷新按钮,彻底交还了学习节奏的控制权。
  • 交错式强化记忆锁 (Interleaved Repetition): 引入了 _repeatCount = 3 机制。在组装待复习快照队列时,并不单纯抽取一遍,而是使用 ...Spread 操作符与外层循环,刻意在底层将复习队列以 [A, B, C, A, B, C, A, B, C] 的交错模式构建。这保证了每日在 FSRS 到期的每一个难点词汇,都会在一轮完整会话中被分散打击并复现 3 次,防止短期记忆作祟,极大地巩固了长期记忆率。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant