【发布时间】:2019-10-18 09:58:14
【问题描述】:
我有一个可变参数提升函数,它允许扁平的单子链没有深度嵌套的函数组合:
const varArgs = f => {
const go = args =>
Object.defineProperties(
arg => go(args.concat(arg)), {
"runVarArgs": {get: function() {return f(args)}, enumerable: true},
[TYPE]: {value: "VarArgs", enumerable: true}
});
return go([]);
};
const varLiftM = (chain, of) => f => { // TODO: replace recursion with a fold
const go = (ms, g, i) =>
i === ms.length
? of(g)
: chain(ms[i]) (x => go(ms, g(x), i + 1));
return varArgs(ms => go(ms, f, 0));
};
它有效,但我想通过折叠从递归中抽象出来。正常折叠似乎不起作用,至少不能与 Task 类型一起使用,
const varLiftM = (chain, of) => f =>
varArgs(ms => of(arrFold(g => mx => chain(mx) (g)) (f) (ms))); // A
因为A 行中的代数会为每次迭代返回一个Task,而不是一个部分应用的函数。
如何用折叠替换非尾递归?
这是当前递归实现的一个工作示例:
const TYPE = Symbol.toStringTag;
const struct = type => cons => {
const f = x => ({
["run" + type]: x,
[TYPE]: type,
});
return cons(f);
};
// variadic argument transformer
const varArgs = f => {
const go = args =>
Object.defineProperties(
arg => go(args.concat(arg)), {
"runVarArgs": {get: function() {return f(args)}, enumerable: true},
[TYPE]: {value: "VarArgs", enumerable: true}
});
return go([]);
};
// variadic monadic lifting function
const varLiftM = (chain, of) => f => { // TODO: replace recursion with a fold
const go = (ms, g, i) =>
i === ms.length
? of(g)
: chain(ms[i]) (x => go(ms, g(x), i + 1));
return varArgs(ms => go(ms, f, 0));
};
// asynchronous Task
const Task = struct("Task") (Task => k => Task((res, rej) => k(res, rej)));
const tOf = x => Task((res, rej) => res(x));
const tMap = f => tx =>
Task((res, rej) => tx.runTask(x => res(f(x)), rej));
const tChain = mx => fm =>
Task((res, rej) => mx.runTask(x => fm(x).runTask(res, rej), rej));
// mock function
const delay = (ms, x) =>
Task(r => setTimeout(r, ms, x));
// test data
const tw = delay(100, 1),
tx = delay(200, 2),
ty = delay(300, 3),
tz = delay(400, 4);
// specialization through partial application
const varAsyncSum =
varLiftM(tChain, tOf) (w => x => y => z => w + x + y + z);
// MAIN
varAsyncSum(tw) (tx) (ty) (tz)
.runVarArgs
.runTask(console.log, console.error);
console.log("1 sec later...");
[编辑] 根据 cmets 的需要,我的折叠实现:
const arrFold = alg => zero => xs => {
let acc = zero;
for (let i = 0; i < xs.length; i++)
acc = alg(acc) (xs[i], i);
return acc;
};
【问题讨论】:
-
这是很多缩小的代码。你能简化这个例子吗?
-
@cristy 那不是缩小的,那是“函数式编程”:)
-
好吧,将你的参数命名为
ms、g、x、f、fm、of、tx、res、@9876543333然后对我来说使用所有这些(几乎一次)看起来像是不必要的缩小,除非你知道它是关于什么的,否则会使代码更难阅读。 -
@Cristy 大多数(如果不是全部的话)在函数式编程中相当地道。类似于您如何命名变量
i,如果它是某种计数器或索引。f- 函数,g也是一个函数(按字母顺序排列在f之后,类似于j在声明i之后用作计数器),fm- 返回 monad 的函数,ms- monads 复数,所以是一个数组,of函数将类型提升到 monad。x- 输入变量的通用名称。res和rej- 结果(成功)和拒绝(失败)。tx、ty、tz- 任务1、任务2、任务3。 -
短名称只是反映了代码的通用性。例如,您可以将
varLiftM与任何 monad 一起使用。通过@VLAZ 完成名称图例:mxmonadic value、msmonadic values、fmmonadic action(返回 monad 的函数)。tx仅表示包装在类型中的值,而没有说明约束(即,如果它是一元的、应用的、函数的、遍历的、可折叠的等)。
标签: javascript recursion functional-programming fold recursion-schemes