作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我目前正在试验延续 monad。 Cont
实际上在 Javascript 中很有用,因为它从回调模式中抽象出来。
当我们处理 monadic 递归时,总会有堆栈溢出的风险,因为递归调用不在尾部位置:
const chain = g => f => k =>
g(x => f(x) (k));
const of = x => k =>
k(x);
const id = x =>
x;
const inc = x =>
x + 1;
const repeat = n => f => x =>
n === 0
? of(x)
: chain(of(f(x))) (repeat(n - 1) (f));
console.log(
repeat(1e6) (inc) (0) (id) // stack overflow
);
MonadRec
操作符的
tailRecM
类型类,它可以为某些 monad 启用尾递归 monadic 计算。所以我尝试主要根据fantasy land规范在Javascript中实现
chainRec
:
const chain = g => f => k => g(x => f(x) (k));
const of = x => k => k(x);
const id = x => x;
const Loop = x =>
({value: x, done: false});
const Done = x =>
({value: x, done: true});
const chainRec = f => x => {
let step = f(Loop, Done, x);
while (!step.done) {
step = f(Loop, Done, step.value);
}
return of(step.value);
};
const repeat_ = n => f => x =>
chainRec((Loop, Done, [n, x]) => n === 0 ? Done(x) : Loop([n - 1, f(x)])) ([n, x]);
console.log(
repeat_(1e6) (n => n + 1) (0) (id) // 1000000
);
Cont
的上下文。在这种情况下,上下文只是“计算的其余部分”,即。函数组合相反,因此返回预期值。但它对任何 monad 都有效吗?
const Bounce = (f,x) => ({ isBounce: true, f, x })
const Cont = f => ({
_runCont: f,
chain: g =>
Cont(k =>
Bounce(f, x =>
Bounce(g(x)._runCont, k)))
})
// ...
const repeat = n => f => x => {
const aux = (n,x) =>
n === 0 ? Cont.of(x) : Cont.of(f(x)).chain(x => aux(n - 1, x))
return runCont(aux(n,x), x => x)
}
chain
以某种方式合并到递归算法中,即可以发生一元效应。不幸的是,我无法破译此运算符或将其与堆栈不安全版本协调(尽管
Bounce(g(x)._runCont, k)
似乎是
f(x) (k)
部分)。
chainRec
的实现或误解了 FL 规范,或者两者兼而有之?
最佳答案
Did I mess up the implementation of
chainRec
, or misunderstood the FantasyLand spec, or both or none of it?
chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b
m
是
Cont
并且
c
是你在
a
或
b
上的 Done/Loop 包装器:
chainRec :: ((a -> DL a b, b -> DL a b, a) -> Cont (DL a b), a) -> Cont b
chainRec
和 repeat
实现根本不使用 continations!
const chainRec = f => x => k =>
f(Loop, Done, x)(step =>
step.done
? k(step.value) // of(step.value)(k)
: chainRec(f)(step.value)(k)
);
chain
从
g => f => k => g(x => f(x)(k))
转换为
g => f => g(f)
(即
g => f => k => g(x => f(x))(k)
)),它看起来像
const chainRec = f => x =>
f(Loop, Done, x)(step =>
step.done
? of(step.value)
: chainRec(f)(step.value)
);
const join = chain(id);
const chainRec = f => x => join(f(chainRec(f), of, x));
ChainRec
背后的想法)
const chainRec = f => x => k => {
let step = Loop(x);
do {
step = f(Loop, Done, step.value)(id);
// ^^^^ unwrap Cont
} while (!step.done)
return k(step.value); // of(step.value)(k)
};
step
调用初始化
f
,
do/while
而不是
do
)并不重要,你的也很好,但重要的部分是
f(Loop, Done, v)
返回一个延续。
repeat
的实现留给读者作为练习:D
f
已经使用延续,它可能会变得更有用,也更容易正确)
关于javascript - 如何为延续 monad 实现堆栈安全的 chainRec 运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48967530/
[编辑] 这是How to implement a stack-safe chainRec operator for the continuation monad?的后续问题 给定的是 chainRe
我目前正在试验延续 monad。 Cont 实际上在 Javascript 中很有用,因为它从回调模式中抽象出来。 当我们处理 monadic 递归时,总会有堆栈溢出的风险,因为递归调用不在尾部位置:
我是一名优秀的程序员,十分优秀!