3个版本
0.1.2 | 2019年8月8日 |
---|---|
0.1.1 | 2019年8月8日 |
0.1.0 | 2019年8月7日 |
#742 in 算法
145KB
789 行
包含 (Mach-o可执行文件, 370KB) fib
Q
Q框架基于一个实验性模拟器,如您在初始代码中所见。虽然它是咖啡崩溃和24小时不睡觉思考的结果,但我还是要感谢其原始创作者。
形式定义
队列自动机可以定义为六元组
- M = ( Q , Σ , Γ , $ , s , δ ) 其中
- Q 是状态集合;
- Σ ⊂ Γ 是输入字母表集合;
- Γ 是队列字母表;
- $ ∈ Γ − Σ 是初始队列符号;
- s ∈ Q 是起始状态;
- δ : Q × Γ → Q × Γ ∗ 是转换函数。
但最有趣的是,它是图灵完备的!
理论
我们可以将队列自动机映射到一个由
- 基于事件的离散事件模拟器
- 队列网络
队列网络本身是一个非常强大的工具,可以模拟包括网络、Web服务、计算机架构在内的许多系统。基于事件的离散事件模拟器可以以较低的成本“执行”此类模型。
将它们结合起来,我们得到了一个非常强大的工具,适用于几乎所有的领域。
演示1: demo.rs
demo.rs
是一个简单的D/D/1模拟器,它可以调整以模拟任何M/G/1或任何网络模型,但在这里我们将其用作调度器。生成器可以被认为是事件源,服务器可以被认为是事件接收器。
$cargo run
Compiling qframework v0.1.0 (/Users/mark/Project/q)
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/demo`
@0000000000 sending [customor 0000] total tx 0
@0000000001 serving [customor 0000] total rx 1
@0000000001 sending [customor 0001] total tx 1
@0000000002 serving [customor 0001] total rx 2
@0000000002 sending [customor 0002] total tx 2
@0000000003 serving [customor 0002] total rx 3
@0000000003 sending [customor 0003] total tx 3
@0000000004 serving [customor 0003] total rx 4
@0000000004 sending [customor 0004] total tx 4
@0000000005 serving [customor 0004] total rx 5
@0000000005 Finished All Events
Send 5, processed 5, tick 5
如果服务器忙碌,则允许服务器丢弃请求,这可以通过在服务器代码上提前2个时钟周期轻松验证。
@0000000021 dropped [customor 0021]
@0000000022 serving [customor 0019] total rx 11
@0000000022 sending [customor 0022] total tx 22
@0000000023 sending [customor 0023] total tx 23
@0000000023 dropped [customor 0023]
@0000000024 serving [customor 0020] total rx 12
@0000000024 sending [customor 0024] total tx 24
@0000000025 sending [customor 0025] total tx 25
演示2: fib.rs
如果这样一个强大的工具只能处理Web请求或模拟队列网络,那就太遗憾了。由于它是图灵完备的,我们可以做更多高级的事情。比如尾递归优化和递归,以及惰性求值?Rust中还有哪些缺失的东西!
是的。
让我们回到旧的fib(n)函数,看看如何在q框架中实现它!
首先我们定义一个名为Fib的东西,另一个名为Add的东西
Fib(n) = Add ( Fib(n -1 ) , Fib (n-2), Fib(0) = 0, Fib(1) = 1。
1:开始 - 将 Event::Fib(8) 添加到事件队列 2:系统启动,选择第一个事件,运行 fib.execute() 3:在 fib.execute() 内部,它只是将 3 个事件添加到列表中:Fib(6)、Fib(7)、Add,现在 Fib(8) 事件已消失,记住我们并没有复制 Fib,只是传递一个 Event 类型 Fib(8)。 4:在 fib.execute() 被调用后,系统从优先队列(已排序)中选择最近的事件,这次是 Fib(6)。 5:Fib(6) 将 3 个事件添加到列表中,现在变为 Fib(7)、Add、Fib(5)、Fib(4)。 6:尝试运行 Add,但我们发现它的队列(只有当我们在其缓冲区中找到两个或更多数字时才执行加法)为空,我们将其推回到事件队列的末尾。 7:重复进行,最终我们得到 Fib(0)、Fib(1)、Add,在 Fib(0) 和 Fib(1) 上,我们将数字写入 Add 的队列。 8:最终 Add 将被执行,它弹出一个数字,将它们相加并将结果推回到它的队列。 9:最终我们在 Add 的队列中有了最后两个数字,并将它们相加,再次将结果写入 Add 的队列。 10:系统中没有更多事件,我们得到了 Add 队列中的结果。
./fib 5
@0000000000 queue = Queue { fibs: [], capacity: 1000 }
@0000000001 queue = Queue { fibs: [], capacity: 1000 }
@0000000002 queue = Queue { fibs: [], capacity: 1000 }
@0000000003 queue = Queue { fibs: [], capacity: 1000 }
@0000000004 queue = Queue { fibs: [], capacity: 1000 }
@0000000005 queue = Queue { fibs: [1], capacity: 1000 }
@0000000006 queue = Queue { fibs: [1], capacity: 1000 }
@0000000007 queue = Queue { fibs: [1, 1], capacity: 1000 }
@0000000008 queue = Queue { fibs: [1, 1], capacity: 1000 }
@0000000009 queue = Queue { fibs: [1, 1], capacity: 1000 }
@0000000010 queue = Queue { fibs: [1, 1], capacity: 1000 }
@0000000011 queue = Queue { fibs: [1, 1], capacity: 1000 }
@0000000011 queue = Queue { fibs: [1, 1], capacity: 1000 }
@0000000012 queue = Queue { fibs: [2], capacity: 1000 }
@0000000013 queue = Queue { fibs: [2, 1], capacity: 1000 }
@0000000014 queue = Queue { fibs: [2, 1], capacity: 1000 }
@0000000014 queue = Queue { fibs: [2, 1], capacity: 1000 }
@0000000015 queue = Queue { fibs: [3], capacity: 1000 }
@0000000015 queue = Queue { fibs: [3], capacity: 1000 }
@0000000016 queue = Queue { fibs: [3, 0], capacity: 1000 }
@0000000017 queue = Queue { fibs: [3, 0], capacity: 1000 }
@0000000017 queue = Queue { fibs: [3, 0], capacity: 1000 }
@0000000018 queue = Queue { fibs: [3], capacity: 1000 }
@0000000018 queue = Queue { fibs: [3, 0], capacity: 1000 }
@0000000019 queue = Queue { fibs: [3], capacity: 1000 }
@0000000020 queue = Queue { fibs: [3, 1], capacity: 1000 }
@0000000021 queue = Queue { fibs: [3, 1], capacity: 1000 }
@0000000022 queue = Queue { fibs: [3, 1], capacity: 1000 }
@0000000022 queue = Queue { fibs: [3, 1], capacity: 1000 }
@0000000023 queue = Queue { fibs: [4], capacity: 1000 }
@0000000023 queue = Queue { fibs: [4, 1], capacity: 1000 }
@0000000024 queue = Queue { fibs: [5], capacity: 1000 }
@0000000025 queue = Queue { fibs: [5, 0], capacity: 1000 }
@0000000026 queue = Queue { fibs: [5, 0], capacity: 1000 }
@0000000026 queue = Queue { fibs: [5, 0], capacity: 1000 }
@0000000027 queue = Queue { fibs: [5], capacity: 1000 }
@0000000027 queue = Queue { fibs: [5, 0], capacity: 1000 }
@0000000028 queue = Queue { fibs: [5], capacity: 1000 }
@0000000029 queue = Queue { fibs: [5, 0], capacity: 1000 }
@0000000030 queue = Queue { fibs: [5, 0], capacity: 1000 }
@0000000030 queue = Queue { fibs: [5, 0], capacity: 1000 }
@0000000031 queue = Queue { fibs: [5], capacity: 1000 }
@0000000031 Finished All Events
Result 5
您可以在纸上手动进行此过程,您会发现我们不需要维护一个深的调用栈,这确实是一种懒加载的方式!
代码在哪里? wip
。
欢迎 PR!
未来计划
目前,这个项目只是处于基本形态,欢迎提出想法。
- 定义 API
- 添加更多示例
- 添加 0 个外部依赖
许可证
MIT/Apache-2.0
行为准则
别在 Reddit 上给我踩分了!
常见问题解答
- 这是不是任何 OTP 中的 actor 的另一个名字?
- 我从未听说过它,我从未使用过 actor,我认为这是编写复杂动态图流程的明显方式,因为在我的前半生中我写 Verilog 并使用 VCS/Modelsim,我知道 DSE 是强大的!
- 请他们为 actor 提供形式化模型并证明它是图灵完备的。
- 为什么这被称为框架?它试图解决什么问题?
- 为了让人们摆脱 ECS/Redux,请这些开发者证明他们的设计模式是图灵完备的。
- 它可能是一个使用宏实现的 Q 语言,但对我来说太复杂了。