【发布时间】:2017-01-18 10:09:48
【问题描述】:
我为Iterables 编写了一个reduce 函数,现在我想派生一个可以映射任意Iterables 的通用map。但是,我遇到了一个问题:由于Iterables 抽象了数据源,map 无法确定它的类型(例如Array、String、Map 等)。我需要这种类型来调用相应的标识元素/concat 函数。想到了三个解决方案:
- 显式传递标识元素/concat 函数
const map = f => id => concat => xs(这很冗长,但会泄漏内部 API) - 仅映射实现 monoid 接口的
Iterables(很酷,但引入了新类型?) - 依赖
ArrayIterator、StringIterator等的原型或构造函数身份。
我尝试了后者,但 isPrototypeOf/instanceof 总是让 false 不管做什么,例如:
Array.prototype.values.prototype.isPrototypeOf([].values()); // false
Array.prototype.isPrototypeOf([].values()); // false
我的问题:
-
ArrayIterator/StringIterator/...的原型在哪里? - 是否有更好的方法来解决给定问题?
编辑: [][Symbol.iterator]() 和 ("")[Symbol.iterator]() 似乎共享相同的原型:
Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) ====
Object.getPrototypeOf(Object.getPrototypeOf(("")[Symbol.iterator]()))
通过原型进行区分似乎是不可能的。
编辑:这是我的代码:
const values = o => keys(o).values();
const next = iter => iter.next();
const foldl = f => acc => iter => {
let loop = (acc, {value, done}) => done
? acc
: loop(f(acc) (value), next(iter));
return loop(acc, next(iter));
}
// static `map` version only for `Array`s - not what I desire
const map = f => foldl(acc => x => [...acc, f(x)]) ([]);
console.log( map(x => x + x) ([1,2,3].values()) ); // A
console.log( map(x => x + x) (("abc")[Symbol.iterator]()) ); // B
A 行中的代码会产生所需的结果。然而,B 产生一个 Array 而不是 String 并且仅串联有效,因为 Strings 和 Numbers 在这方面巧合地等效。
编辑:我这样做的原因似乎很混乱:我想使用可迭代/迭代器协议来抽象迭代细节,以便我的折叠/展开和派生映射/过滤器等功能是通用的。问题是,如果没有身份/连接协议,您就无法做到这一点。而我依赖原型身份的小“黑客”并没有成功。
@redneb 在他的回复中提出了一个很好的观点,我同意他的观点,不是每个可迭代的也是“可映射的”。然而,牢记这一点,我仍然认为以这种方式利用协议是有意义的——至少在 Javascript 中——直到将来的版本中可能存在用于这种用途的可映射或收集协议。
【问题讨论】:
-
你指的那些
Iterable/ArrayIterator/StringIterator接口的来源是什么?它们来自一些标准的 javascript 框架吗?你自己定义了吗? -
没有
ArrayIterator或StringIterator原型,有迭代协议:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… -
@micnic @redneb
[].values()在我的 chromium 浏览器中记录ArrayIterator {}。这仅仅是 chrome 特有的行为吗? -
("")[Symbol.iterator]()记录StringIterator {}。
标签: javascript functional-programming ecmascript-6 iteration iterable