前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >算法演绎 | 巧妙的 Completer 完成器

算法演绎 | 巧妙的 Completer 完成器

作者头像
张风捷特烈
发布2024-02-07 07:31:50
920
发布2024-02-07 07:31:50
举报
文章被收录于专栏:Android知识点总结
0. 缘起

最近几个月接触了些算法题,然后想个有趣的点子:

是否可以用 Flutter 做一个交互应用,可视化 地展示算法执行过程中的变量的信息。

比如拿一个最简单的累加算法来说,启动算法之后,每次点击下一步,界面上会展示出该步对应的变量信息。就可以可视化地呈现出一个算法运算过程中变量的变化情况。

代码语言:javascript
复制
int sum(int count) {
  int sum = 0;
  for (int i = 0; i < count; i++) {
    sum = sum + i;
  }
  return sum;
}

当算法执行完毕后,每一步都会被记录,可以左右切换,查看对应步数的变量情况(如下右图):

算法演绎过程

演绎结束后切换查看帧

1. 对数据的定义

帧 Frame : 记录算法执行一步中的所有数据 节点 Node : 一帧中的变量信息单体数据

目前的节点 Node 只是展示变量名和对应的值,未来可以拓展其他类型的节点,自己绘制需要展示的内容。一帧 Frame 由若干个 Node 构成:

代码语言:javascript
复制
class Frame{
  final List<Node> nodes;

  Frame(this.nodes);
}

class Node {
  final String name;
  final String value;

  Node({
    required this.name,
    required this.value,
  });
}

数据定义完了,接下来重点就是如何在一个方法运行期间,收集每一帧的数据。

2. 对数据的收集

拿 sum 算法来说,每一步执行时机我们是知道的。如下所示,我们可以在第四行下方得到每帧的数据:

这样很自然地可以想到:可以执行一下 sum 方法,然后用的列表收集所有的 Frame 数据。但这样有一些缺陷,一方面:我们无法控制算法执行的过程,无法中断算法;另一方面, 这样会一次性把所有的 Frame 收集起来,如果是上百亿的数据,内存会吃不消。

代码语言:javascript
复制
int sum(int count) {
  int sum = 0;
  for (int i = 0; i < count; i++) {
    sum = sum + i;
    /// 收集当前帧信息
    List<Node> nodes = [
      Node(name: 'i', value: i.toString()),
      Node(name: 'sum', value: sum.toString()),
      if (i == count - 1) Node(name: 'return', value: sum.toString()),
    ];
    Frame frame = Frame(nodes);
  }
  return sum;
}

有什么方式,可以暂停代码的执行,并且可以在之后的交互中恢复执行呢?答案是 Completer,它可以让代码进行异步地等待,直到 Completer 对象的完成。这里将充分发挥 Completer 的价值,让你对它有深刻地认知。

代码处理如下所示,定义一个 AlgoFrameCallback 的异步回调函数,向外界暴露算法执行过程中的 Frame 数据。回调返回 bool 值,返回 true 时表示希望停止算法,直接返回。由于这里通过 await 等待异步回调执行完毕,所以每一帧都会异步阻塞而暂停,等待下一步的时异步任务完成的时机。

代码语言:javascript
复制
typedef AlgoFrameCallback = Future<bool> Function(Frame frame);

Future<int?> sum(int count, AlgoFrameCallback callBack) async {
  int sum = 0;
  for (int i = 0; i < count; i++) {
    sum = sum + i;
    // 回调本次循环的变量信息
    /// 收集当前帧信息
    List<Node> nodes = [
      Node(name: 'i', value: i.toString()),
      Node(name: 'sum', value: sum.toString()),
      if (i == count - 1)Node(name: 'return', value: sum.toString()),
    ];
    Frame frame = Frame(nodes);
    bool end = await callBack(frame);
    if(end) return null;
  }

  return sum;
}

3. Completer 的使用

下面代码中 startSumProgram 方法会启动 sum 算法触发的 Frame 回调,通过 _onFrameTick 异步方法进行监听。每次接收到 Frame 时,将其加入到 _frames 列表中,并更新界面;然后返回 _completer.future,就可以让 sum 中的回调逻辑异步阻塞,来等待 _completer 的完成。

代码语言:javascript
复制
final List<Frame> _frames = [];
final int _counter = 10;
Completer<bool>? _completer;

void startSumProgram() {
  _completer?.complete(true);
  _completer = Completer();
  _frames.clear();
  sum(_counter, _onFrameTick);
}

Future<bool> _onFrameTick(Frame frame) {
  _frames.add(frame);
  setState(() {});
  return _completer!.future;
}

想要执行到下一帧,需要让 _completer 完成,也就是下一步时需要做的工作。点击时触发 _next 方法,使用 _completer#complete 方法完成,然后重新创建下一帧的完成器,继续阻塞下一帧的前进,从而完成需求。

代码语言:javascript
复制
void _next() {
   _completer?.complete(false);
   _completer = Completer();
}

4. 算法运行状态

ProgramStatus 是用于记录算法运行状态的枚举,用于确定运行按钮的激活状态,以及交互过程中的流程控制。比如在算法运行(running)的过程中,无法后退;算法演绎结束,可以根据记录的帧来前进和后退。

Y演绎中Y

YA

代码语言:javascript
复制
enum ProgramStatus {
  none,
  running,
  success,
}

我们需要在不同的时机维护 ProgramStatus状态的正确性,比如开启算法是置为 running 状态:

image.png
image.png

当算法运行完毕,置为 success 状态。这样就可以根据算法的运行状态,维护界面构建的逻辑。

image.png
image.png

根据算法运行的状态,也可以控制业务逻辑的代码;比如下一帧方法在算法完成后,需要通过 _frames 列表根据激活索引来更新当前帧。因为算法运行完毕,_completer 的完成就无法驱动下一帧了。

代码语言:javascript
复制
void _next() {
  if(programStatus==ProgramStatus.success){
    activeFrameIndex++;
    setState(() {});
  }else{
    _completer?.complete(false);
    _completer = Completer();
  }
}

late int activeFrameIndex = _counter-1;

Frame get activeFrame{
  if(programStatus==ProgramStatus.success){
    /// 结束演绎,通过索引查询帧记录
   return _frames[activeFrameIndex];
  }
  return _frames.last;
}

到这里 ,一个最简单的算法演绎就完成了。可以在界面上展示算法执行过程中的变量变化细节。这样对算法的理解非常有帮助,当然这只是一个开始,验证了算法演绎的可行性。后续还会构思一下根据 Frame 来自己绘制信息,这样数组、队列、栈、链表、二叉树等相关算法就可以可视化展示。那到这里本文就结束啦,谢谢观看~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 缘起
  • 1. 对数据的定义
  • 2. 对数据的收集
    • 3. Completer 的使用
      • 4. 算法运行状态
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档