javascript - Javascript ES6:为展开函数实现生成器
问题描述
我正在尝试重构此代码,该代码定义了一个unfold
函数并使用它来制作该count
函数,该函数用最多计数的数字填充数组。我不想调用,而是count(100)
想将 count 变成一个可以通过next()
任意调用来使用的生成器。
function unfold (fn, state) {
return fn(
(value, nextState) => {
return [ value, ...unfold (fn, nextState)]
},
()=>[],
state
);
}
function count (max) {
return unfold(
(next, done, state)=>{
return state >= max ?
done() :
next(state, state +1)
},
0
);
}
这里的流程已经有点难以理解,我很难弄清楚 yield 语句的流程应该如何工作。我想产生结果数组,它是unfold
函数的第 4 行,return [ value, ...unfold (fn, nextState)]
但我不确定如何将它一直传递给 count 函数。
这是我到目前为止所拥有的,但它只是返回一个带有生成器的生成器,然后在几次next
调用后结束:
function * _unfold (fn, base) {
yield * fn(
(value, nextState)=>([ value, ..._unfold (fn, nextState)]),
base
)
return [];
}
function * count (max) {
yield * _unfold(
compress,
0
);
return 0;
}
function * compress (next, state) {
yield next(state, state +1)
return null;
}
解决方案
我想向您展示一个尽可能接近 FP 中的原始展开实现的实现。希望从那里您可以使用命令式生成器来实现它。
这是第一个版本unfoldr
:
unfoldr = f => state => {
const go = ([x, state_]) =>
state_ === undefined
? []
: arrCons(x) (go(f(state_)));
// ^^^^^^^^^^^^^ strictly evaluated
return go(f(state));
};
展开是一个本质上是无限的过程,因此你需要惰性来阻止它。更准确地说,您需要一个构建结构的函数,该结构的第二个参数是非严格的。arrCons
在这两个参数中都可以是非严格的,因为它所做的只是将它们存储在类似对的数据类型中。但是,Javascript 是经过严格评估的。
假设我们有一个thunk
向 Javascript 引入隐式 thunk 的函数,即一个可以在没有括号的情况下调用的空函数,就像对象上的惰性 getter。它只需要一个普通的空函数并将其转换为隐式函数。这是我们的更新unfoldr
:
unfoldr = f => state => {
const go = ([x, state_]) =>
state_ === undefined
? []
: arrCons(x) (thunk(() => go(f(state_))));
return go(f(state));
};
现在我们模拟了非严格评估,递归步骤中的表达式被评估得恰到好处,即简化为形式[x, Thunk]
仅此而已。请注意,我们用它[]
来表示基本情况,从而表示展开过程的结束。我们宁愿用标记的联合来编码这种行为,即Option
/Maybe
类型。但为了简单起见,我将实现保持原样。
unfoldr
以下是定义斐波那契数列如何使用的示例:
const arrCons = head => tail =>
[head, tail];
const unfoldr = f => state => {
const go = ([x, state_]) =>
state_ === undefined
? []
: arrCons(x) (thunk(() => go(f(state_))));
return go(f(state));
};
const fibs = unfoldr(
([x, y]) => [x, [y, x + y]]) ([0, 1]);
const main = fibs[1] [1] [1] [1] [1] [1] [1] [1] [1] [1]; // [55, Thunk]
main[0]; // 55
这是thunk
返回 a的完整实现Proxy
:
const thunk = f =>
new Proxy(f, new ThunkProxy(f));
const THUNK = "scriptum_thunk";
class ThunkProxy {
constructor(f) {
this.memo = undefined;
}
apply(g, that, args) {
if (this.memo === undefined)
this.memo = g();
return this.memo(...args);
}
defineProperty(g, k, descriptor) { debugger;
if (this.memo === undefined)
this.memo = g();
Object.defineProperty(this.memo, k, descriptor);
return true;
}
get(g, k) {
if (this.memo === undefined)
this.memo = g();
if (k === THUNK)
return true;
else if (k === Symbol.toPrimitive)
return () => this.memo;
else if (k === "valueOf")
return () => this.memo;
else return this.memo[k];
}
has(g, k) {
if (this.memo === undefined)
this.memo = g();
return k in this.memo;
}
set(g, k, v) {
if (this.memo === undefined)
this.memo = g();
this.memo[k] = v;
return true;
}
}
const arrCons = head => tail =>
[head, tail];
const arrUnfoldr = f => state => {
const go = ([x, state_]) =>
state_ === undefined
? []
: arrCons(x) (thunk(() => go(f(state_))));
return go(f(state));
};
const fibs = arrUnfoldr(
([x, y]) => [x, [y, x + y]]) ([0, 1]);
const main = fibs[1] [1] [1] [1] [1] [1] [1] [1] [1] [1]; // [55, Thunk]
console.log(main[0]);
推荐阅读
- python - 在 python 中升级 pytesseract
- c# - C#是否有一个选项可以查看文件更改而不在更改事件后重新打开它
- visual-studio-code - VScode - 无法恢复正常的移位箭头选择
- java - 为具有多对一关系的列保存对象时出错
- google-sheets - 3 维动态查找 - Google 表格
- javascript - Jquery点击李不做显示/隐藏,因为它应该
- pandas - 计算带条件的多索引系列/数据框
- loops - E0597 试图循环一个向量
- webhooks - Spinnaker webhook 阶段不允许超过 5 分钟的超时
- azure - 我可以使用 RBAC 访问 Azure 服务总线中继吗?