1

在我之前的问题中:

从没有数组的函数链中提取数据

@Aadit M Shah 给了我惊人的解决方案,如下所示:

https://stackoverflow.com/a/51420884/6440264

给定一个像A(a)(b)(f)where fis a function 这样的表达式,不可能知道是否f应该将其添加到列表中,或者它是否是归约函数。因此,我将描述如何编写表达式,例如A(a)(b)(f, x)which 等价于[a, b].reduce(f, x). 这使我们能够根据您提供的参数数量来区分列表何时结束:

const L = g => function (x, a) {
    switch (arguments.length) {
    case 1: return L(k => g((f, a) => k(f, f(a, x))));
    case 2: return g((f, a) => a)(x, a);
    }
};

const A = L(x => x);

const xs = A(1)(2)(3)(4)(5);

console.log(xs((x, y) => x + y, 0));        // 15
console.log(xs((x, y) => x * y, 1));        // 120
console.log(xs((a, x) => a.concat(x), [])); // [1,2,3,4,5]

它由于延续而起作用。每次我们添加一个新元素,我们都会累积一个 CPS 函数。每个 CPS 函数调用前一个 CPS 函数,从而创建一个 CPS 函数链。当我们给这个 CPS 函数链一个基函数时,它会展开链并允许我们减少它。换能器镜头背后的想法相同。

我还有2个问题。

  1. 为了区分 reduce 函数,我考虑了一些使用反射的自定义 Typing 机制,但是为了专注于这个问题,到目前为止我想简单地应用

    const isReducer = f => (typeof f === 'function');

  2. 提供初始值的要求对折叠/归约有限制,例如,不可能为归约提供二进制操作的初始值,例如

    常量头 = (a, b) => a; 常量尾 = (a, b) => b;

(除非您手动提供第一个/最后一个值,这对运行代码没有意义)理论上,每个二进制操作都有一个标识值,但是不可能按原样提供。唯一的方法是抽象为一个身份。

话虽如此,我无法将提供的代码重构为单个参数和函数的 reducer 类型,并将默认值作为序列的初始值。

你能提供重构的代码吗?此外,此示例的传感器/CPS 的任何信息都值得赞赏。

4

1 回答 1

0

当你不提供初始值时,你会失去很多力量。例如,您将无法将列表转换为数组。这是因为返回类型必须与列表元素的类型相匹配。考虑:

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f a []     = a
foldl f a (x:xs) = foldl f (f a x) xs

foldl1 :: (a -> a -> a) -> [a] -> a
foldl1 f (x:xs) = foldl f x xs

如您所见,的返回类型foldl可以是任何b独立于类型的类型a。但是,返回类型foldl1强制为a. 因此,如果您有一个列表,A(1)(2)(3)(4)(5)那么当您折叠此列表时,结果必然是一个数字。

A(1)(2)(3)(4)(5)(concat2)您可以通过执行类似where的操作来摆脱它,const concat2 = (x, y) => [].concat(x, y)因为 JavaScript 不是一种强类型语言。但是,它并不一致,因为A([1,2,3])([4,5,6])([7,8,9])(concat2)计算结果为[1,2,3,4,5,6,7,8,9]而不是[[1,2,3],[4,5,6],[7,8,9]]. 如果您没有初始值,我看不到任何将第二个列表转换为数组同时保留其结构的方法。


尽管如此,如果你仍然想这样做,那么你几乎不需要改变。请注意,foldl1仅代表工作于foldl. 因此,我们只需要将列表的第一个元素与其他元素分开,并将其用作累加器的初始值:

const L = g => a => x => typeof x !== "function" ?
    L((f, a) => f(g(f, a), x))(a) :
    g(x, a);

const A = L((f, a) => a);

const xs = A(1)(2)(3)(4)(5);

console.log(xs((x, y) => x + y)); // 15
console.log(xs((x, y) => x * y)); // 120

最后,如果您真的想了解函数式编程和延续,那么我建议您阅读SICP(计算机程序的结构和解释)或HtDP(如何设计程序)。HtDP 通常被认为对初学者更友好。但是,我强烈推荐阅读 SICP。

于 2018-07-19T18:21:58.580 回答