- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
reduce
(在 FP 中又称为 foldL
)是 Javascript 中最通用的迭代高阶函数。例如,您可以根据 reduce
实现 map
或 filter
。我使用命令式循环来更好地说明算法:
const foldL = f => acc => xs => {
for (let i = 0; i < xs.length; i++) {
acc = f(acc)(xs[i]);
}
return acc;
};
const map = f => xs => {
return foldL(acc => x => acc.concat([f(x)]))([])(xs);
}
let xs = [1, 2, 3, 4];
const inc = x => ++x;
const result = map(inc)(xs);
console.log(result); // [2, 3, 4, 5]
但是您不能从 reduce
派生出 some
或 every
,因为两者都能够提前返回。
那么更通用的部分归约函数是什么样子的呢?到目前为止,我已经提出了以下天真的实现:
const foldLP = f => pred => acc => xs => {
for (let i = 0, r; i < xs.length; i++) {
r = pred(i, acc, xs[i]);
if (r === true) { // normal iteration
acc = f(acc)(xs[i]);
} else if (r === false) { // early exit
break;
} /* else { // skip iteration
continue;
} */
}
return acc;
};
const takeN = n => (idx, acc, x) => idx < n;
const append = xs => ys => xs.concat(ys);
let xs = [1,2,3,4,5];
const result = foldLP(append)(takeN(3))([])(xs);
console.log(result); // [1,2,3]
我还可以根据 foldLP
实现 map
:
const always = x => y => x;
const map = f => xs => {
return foldLP(acc => x => acc.concat([f(x)]))(always(true))([])(xs);
}
map(inc)(xs); // [2,3,4,5,6]
缺点很明显:只要不需要提前退出机制,就会不必要地调用always
。转换函数和提前退出函数由foldLP
静态组合而成,不能单独使用。是否有更高效的组合器,可以实现通用的 Array.prototype.reduce
?
如果你查看调用堆栈,reducing 函数 acc => x => acc.concat([f(x)])
的返回语句将不得不跳过几个堆栈帧.这种堆栈操作让我想到了延续。也许有一种有效的方法可以解决 Continuation Passing Style 中的这个问题,同时使用经过调整的 call/cc 函数 - 或者至少使用生成器。
最佳答案
事实证明,一旦您习惯了 CPS,就可以很容易地实现 reduce 的泛化:
const foldL = f => acc => xs => xs.length
? f(acc)(xs[0])(xss => foldL(f)(xss)(xs.slice(1)))
: acc;
const map = f => foldL(acc => x => cont => cont(acc.concat([f(x)])))([]);
const filter = pred => foldL(acc => x => cont => cont(pred(x) ? acc.concat([x]) : acc))([]);
const every = pred => foldL(acc => x => cont => pred(x) ? cont(true) : false)(true);
const some = pred => foldL(acc => x => cont => pred(x) ? true : cont(false))(false);
const takeN = n => foldL(acc => x => cont => acc.length < n ? cont(acc.concat([x])) : acc)([]);
const comp = f => g => x => f(g(x));
const not = f => x => !f(x);
const inc = x => ++x;
const odd = x => x & 1;
const even = not(odd);
const lt3 = x => x < 3;
const eq3 = x => x === 3;
const sqr = x => x * x;
const xs = [1, 2, 3, 4, 5];
map(inc)(xs); // [2, 3, 4, 5, 6]
filter(even)(xs); // [2, 4]
every(lt3)(xs); // false
some(lt3)(xs); // true
takeN(3)(xs); // [1, 2, 3]
// we can compose transforming functions as usual
map(comp(inc)(sqr))(xs); // [2, 5, 10, 17, 26]
// and the reducing functions as well
comp(map(inc))(filter(even))(xs); // [3, 5]
comp(takeN(2))(filter(odd))(xs); // [1, 3]
如您所见,这并不是真正的纯 CPS,而是混合了 Direct Style。这有一个很大的好处,即 foldL
和通常的转换函数不必携带额外的延续参数,而是保持它们的正常签名。
我只在部分代码中使用 CPS 函数,它们对于实现所需的行为是不可替代的。 CPS 是一种极其强大的结构,您总是会尽可能使用表达能力最低的结构。
comp(takeN(2))(filter(odd))(xs)
说明了该实现的一个弱点(可能还有其他弱点)。归约函数的组合不发生在数组元素的级别。因此,在计算最终结果 ([1, 3]
) 之前,需要一个中间数组 ([1, 3, 5]
)。但这是换能器的问题......
关于javascript - 如何实现更通用的 reduce 功能以允许提前退出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34976936/
如果这有点含糊,我提前道歉,但我什至缺乏如何处理这个问题的基本想法 - 甚至不知道是否有合适的术语可供搜索。 我正在尝试编写一个按时间顺序排列的动画事件的表格驱动系统,其中描述性评论也从表格中提取出来
我是一个老狗,30 年前就用过 BASIC >。我以前在 python 中使用 for 循环时遇到过这种情况,但我选择这个例子是因为我担心循环: 我想解析一个长字符串,其中包含用逗号分隔的双引号中的单
我需要获取从当天算起的 5 个工作日的数组。 今天是:06/04/2018 我需要的输出是: { 0: 06/01/2018, //fri. 1: 05/31/2018, //th
我习惯于使用时间戳,我现在将尝试使用正常日期 2011-02-02 12:00:00 格式 我有这个: SELECT * FROM users_calenders WHERE date ? 我想选择日
我在我的本地仓库 (test-branch) 中创建了一个测试分支,并将其推送到 Github。 如果我转到我的 Github 帐户并选择这个 test-branch 它会显示信息: This bra
我正在尝试设置 JSON 对象的结束日期。结束日期等于开始日期后 30 天。有时这会返回正确的日期,有时则不会。 这是GetDateSchedulerFormatted函数 GetDateSchedu
我有一个执行器服务,它定期执行一堆任务。它们在启动时初始化并经常运行,到目前为止一切顺利。 我现在想添加功能来根据事件快速启动这些任务的执行。 我找到了decorateTask方法,它允许我存储我安排
我需要比当前日期提前 3 周的日期。并将所有日期添加到数组中。我怎样才能得到这个? let date = NSDate() let calendar = NSCalendar(cal
我正在使用以下代码设置日期对象: NSDate *date = [NSDate date]; NSCalendar *gregorian = [[NSCalendar alloc] i
我正在将字符串“Jun 11, 2012 9:30 PM”转换为 NSDate,并且由于某种原因我一直提前 4 小时。有趣的是,我正在使用相同的字符串在详细 View 中提供 UIDatePicker
我的理解是 Xamarin 的提前 (AOT) 编译器将 Xamarin.iOS 应用程序直接编译为 native ARM 汇编代码 (How Xamarin works) . 然而,我不明白的是为什
Angular 2 带有称为提前 (AoT) 的新功能。但是看了一番,还是不能真正理解。它是如何工作的?它将如何带来更好的性能?它与 JIT 有何不同? 谢谢。 最佳答案 Angular 在模块、指令
我看到了一些关于如何纠正这个问题的答案。我有一个 DateTime 类型的对象。我已分配该对象,如下所示。 obj.TimeStamp = DateTime.UtcNow; 我似乎找不到正确的组合或代
我是 Fortran 新手,我不明白这一行: write(*,'(a35)', advance='no') 在: program democonvertion implicit none
我一直在寻找如何做一些像 facebook 新闻提要这样的“高级”列表,但我认为我没有使用正确的关键字来搜索如何做到这一点。我对 android 环境还是很陌生。 这就是我要实现的目标: 我怎样才能得
我有一个包含 2 列的 pandas Dataframe。其中一个是日期格式的索引,另一个是比率 R(0 到 1 之间的数字)。如何向 pandas Dataframe 添加另一列,其中包含一天前一周
我有 2 个媒体查询大小 - only screen and (min-width: 980px)and (max-width: 1499px)"; only screen and (min-widt
我发现了这个: Is AOT (ahead of time) compilation available (or planned) in mono for android? 但是这个问题很老了。 在单
在我看来,当我调用 JTree.expandPath( path ) 默认情况下,它的所有父级也会展开。但我真正想做的是,设置特定的隐形 child 提前展开。这样当一个节点展开时,它的完整子树就会弹
我的时差显示不正确的输出,我正在尝试计算 startTime 和 endTime 之间的时差。 Date time1, time2; long difference; Simp
我是一名优秀的程序员,十分优秀!