这是之后的顺序问题
和
在这里,我想对我的问题的贡献者表示尊重和感谢,特别是@Aadit M Shah 和@user633183
现在,这个问题被打开以澄清差异列表和教会列表之间的异同或关系
.
差异列表
https://stackoverflow.com/a/51320041/6440264
差异列表是一个函数,它接受一个列表并将另一个列表添加到它前面。例如:
const concat = xs => ys => xs.concat(ys); // This creates a difference list.
const f = concat([1,2,3]); // This is a difference list.
console.log(f([])); // You can get its value by applying it to the empty array.
console.log(f([4,5,6])); // You can also apply it to any other array.
差异列表很酷的一点是它们形成了一个幺半群,因为它们只是内函数:
const id = x => x; // The identity element is just the id function.
const compose = (f, g) => x => f(g(x)); // The binary operation is composition.
compose(id, f) = f = compose(f, id); // identity law
compose(compose(f, g), h) = compose(f, compose(g, h)); // associativity law
更好的是,您可以将它们打包成一个简洁的小类,其中函数组合是点运算符:
class DList {
constructor(f) {
this.f = f;
this.id = this;
}
cons(x) {
return new DList(ys => this.f([x].concat(ys)));
}
concat(xs) {
return new DList(ys => this.f(xs.concat(ys)));
}
apply(xs) {
return this.f(xs);
}
}
const id = new DList(x => x);
const cons = x => new DList(ys => [x].concat(ys)); // Construct DList from value.
const concat = xs => new DList(ys => xs.concat(ys)); // Construct DList from array.
id . concat([1, 2, 3]) = concat([1, 2, 3]) = concat([1, 2, 3]) . id // identity law
concat([1, 2]) . cons(3) = cons(1) . concat([2, 3]) // associativity law
您可以使用该
apply
方法检索DList
如下的值:
class DList {
constructor(f) {
this.f = f;
this.id = this;
}
cons(x) {
return new DList(ys => this.f([x].concat(ys)));
}
concat(xs) {
return new DList(ys => this.f(xs.concat(ys)));
}
apply(xs) {
return this.f(xs);
}
}
const id = new DList(x => x);
const cons = x => new DList(ys => [x].concat(ys));
const concat = xs => new DList(ys => xs.concat(ys));
const identityLeft = id . concat([1, 2, 3]);
const identityRight = concat([1, 2, 3]) . id;
const associativityLeft = concat([1, 2]) . cons(3);
const associativityRight = cons(1) . concat([2, 3]);
console.log(identityLeft.apply([])); // [1,2,3]
console.log(identityRight.apply([])); // [1,2,3]
console.log(associativityLeft.apply([])); // [1,2,3]
console.log(associativityRight.apply([])); // [1,2,3]
与常规列表(功能列表,而不是 JavaScript 数组)相比,使用差异列表的一个优点是串联效率更高,因为列表是从右到左串联的。因此,如果您连接多个列表,它不会一遍又一遍地复制相同的值。
教会编码列表
作为使用 Church 对进行编码的替代方案,可以通过使用右折叠函数对其进行识别来对列表进行编码。例如,一个包含三个元素 x、y 和 z 的列表可以由一个高阶函数编码,该函数在应用于组合子 c 和值 n 时返回 cx (cy (czn))。
https://stackoverflow.com/a/51420884/6440264
user633183 的解决方案非常出色。它使用Church 编码的列表使用右折叠来减轻对延续的需求,从而使代码更简单且易于理解。这是她的解决方案,修改后
foldr
看起来像foldl
:
const L = g => function (x, a) {
switch (arguments.length) {
case 1: return L((f, a) => f(g(f, a), x));
case 2: return g(x, a);
}
};
const A = L((f, a) => a);
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]
这
g
是迄今为止积累的 Church 编码列表。最初,它是空列表。g
跟注从右边弃牌。但是,我们也从右侧构建列表。因此,由于我们编写它的方式,似乎我们正在构建列表并从左侧折叠它。
如果所有这些功能都让您感到困惑,那么 user633183 真正在做的是:
const L = g => function (x, a) {
switch (arguments.length) {
case 1: return L([x].concat(g));
case 2: return g.reduceRight(x, a);
}
};
const A = L([]);
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]
如您所见,她正在向后构建列表,然后使用
reduceRight
向后折叠向后列表。因此,看起来您正在向前构建和折叠列表。
我喜欢在差异列表中看到的是
- 这似乎是自然而直接的理解。
- 通过连接(展平),它形成幺半群
- 标识元素是标识函数,不需要提供外部初始值。
我不喜欢什么
- 至少,提供的示例代码依赖于 JavaScript Array
事实上,我喜欢/不喜欢教堂列表的内容与上述内容相反。
我喜欢
- 它独立于 JavaScript Array 实现,它可以自己定义操作:user633183's solution
我不喜欢
- 不知道为什么一定不是左折而是右折?
可以通过使用右折叠函数识别列表来对列表进行编码
不清楚与 Monoids 的关系
特别是,Nil 不是标识元素(= 标识函数),示例代码需要提供外部初始值。
所以,我很好奇的是有没有像 Church-list 这样的 Diffrence 列表的形式化。
规格是
基本上,这是一个差异列表
JavaScipt Array 实现的独立性
初始值是内置的标识函数。
谢谢你。