Typescript 中可迭代的多次遍历
Multiple traversals over iterable in Typescript
在 Typescript 中处理 Iterable
多次遍历的好方法是什么?
一般来说,一个Iterable
只能遍历一次,之后不会产生新的元素。例如,IterableIterator
是 Iterable
与此 属性。因此,多次遍历序列的一种解决方案是首先将其复制到数组(比方说)。
当Iterable
已经可以遍历多次,比如Array,这个复制是多余的。因此,作为一种优化,似乎值得创建一个 possiblyCopyForMultipleTraversals()
函数,仅在必要时才 returns 复制。此函数的保守实现将检测标准集合(例如 Arrays 和 Sets)并避免为它们复制。这样的功能能不能通用实现?
这不一定是完美的,但符合 "create one yourself":
你可以制作一个 MultipleIterable
接口,它只公开一个 属性,其值为 true
,如果对象表示一个 Iterable
,其迭代器方法产生一个 "fresh"每次调用的迭代器:
export interface MultipleIterable<T> extends Iterable<T> {
multipleIterable: true;
}
还有一个函数,您可以调用 Iterable
来检查它是否也是 MultipleIterable
:
function isMultableIterable<T>(iterable: Iterable<T>): iterable is MultipleIterable<T> {
return (iterable) && ((iterable as any).multipleIterable === true);
}
然后,如果需要,您可以扩充 Array
的原型,以表明数组是 MultipleIterable
:
declare global {
interface Array<T> {
multipleIterable: true;
}
}
Array.prototype.multipleIterable = true;
同样,您可以修改以这种方式运行的任何内置可迭代对象的原型。
然后,您可以创建一个 ReplayableIterable<T>
class,其构造函数采用任何 Iterable<T>
并以其迭代器始终新鲜的方式包装它:
class ReplayableIterable<T> implements MultipleIterable<T> {
multipleIterable = true as true;
[Symbol.iterator](): Iterator<T> {
let cur = 0;
let iterable = this;
return {
next(): IteratorResult<T> {
while (cur >= iterable.iteratorResults.length) {
iterable.iteratorResults.push(iterable.iterator.next());
}
const ret: IteratorResult<T> = iterable.iteratorResults[cur];
cur++;
return ret;
}
}
}
private iterator: Iterator<T>;
private iteratorResults: Array<IteratorResult<T>>;
constructor(iterable: Iterable<T>) {
this.iterator = iterable[Symbol.iterator]();
this.iteratorResults = [];
}
}
想法是传入的迭代器只用于获取一个迭代器,处理该迭代器的结果存储在一个数组中。 ReplayableIterable
分发的迭代器仅在耗尽数组内容时才咨询实际的迭代器。这是 lazily 复制迭代器:如果传入的可迭代对象有十亿个元素(或更糟,无限个),但您只打算遍历其中的几个,则不必复制您永远不会使用的元素会占用内存和时间(对于永不终止的生成器来说,时间占用确实相当可观)。
最后,导出一个将任何 Iterable
转换为 MultipleIterable
的函数,方法是返回原样的参数(如果你给它一个 MultipleIterable
开始)或以此为基础构建ReplayableIterable
export function toMultipleIterable<T>(iterable: Iterable<T>): MultipleIterable<T> {
return isMultableIterable(iterable) ? iterable : new ReplayableIterable(iterable);
}
希望对您有所帮助;祝你好运!
也许这是要走的路:
type FIterable<T> = (() => Iterable<T>) | Iterable<T>;
export function* run<T = any>(...its: FIterable<T>[]) {
while (true) {
for (const it of its) {
if (typeof it === 'function') {
yield* it(); // this throw a type error but compile :)
} else {
yield* it;
}
}
}
}
// Example:
export const map: Map<number, number> = new Map([[1, 2], [3, 4]]);
const a = run(() => map.keys(), [5, 6, 7], new Set([8, 9]));
for (const _ of Array(10)) {
console.log(a.next());
};
JS/TS 生成器可以使用 yield*
迭代生成器。
单向迭代的问题应该用函数解决,为什么不重新创建一个迭代?
本例中只有地图不能重复
我的tsconfig.json
{
"compilerOptions": {
"module": "UMD",
"target": "es5",
"lib": [
"es2015",
"es2015.iterable",
"dom"
]
},
"files": [
"app.ts"
]
}
在 Typescript 中处理 Iterable
多次遍历的好方法是什么?
一般来说,一个Iterable
只能遍历一次,之后不会产生新的元素。例如,IterableIterator
是 Iterable
与此 属性。因此,多次遍历序列的一种解决方案是首先将其复制到数组(比方说)。
当Iterable
已经可以遍历多次,比如Array,这个复制是多余的。因此,作为一种优化,似乎值得创建一个 possiblyCopyForMultipleTraversals()
函数,仅在必要时才 returns 复制。此函数的保守实现将检测标准集合(例如 Arrays 和 Sets)并避免为它们复制。这样的功能能不能通用实现?
这不一定是完美的,但符合 "create one yourself":
你可以制作一个 MultipleIterable
接口,它只公开一个 属性,其值为 true
,如果对象表示一个 Iterable
,其迭代器方法产生一个 "fresh"每次调用的迭代器:
export interface MultipleIterable<T> extends Iterable<T> {
multipleIterable: true;
}
还有一个函数,您可以调用 Iterable
来检查它是否也是 MultipleIterable
:
function isMultableIterable<T>(iterable: Iterable<T>): iterable is MultipleIterable<T> {
return (iterable) && ((iterable as any).multipleIterable === true);
}
然后,如果需要,您可以扩充 Array
的原型,以表明数组是 MultipleIterable
:
declare global {
interface Array<T> {
multipleIterable: true;
}
}
Array.prototype.multipleIterable = true;
同样,您可以修改以这种方式运行的任何内置可迭代对象的原型。
然后,您可以创建一个 ReplayableIterable<T>
class,其构造函数采用任何 Iterable<T>
并以其迭代器始终新鲜的方式包装它:
class ReplayableIterable<T> implements MultipleIterable<T> {
multipleIterable = true as true;
[Symbol.iterator](): Iterator<T> {
let cur = 0;
let iterable = this;
return {
next(): IteratorResult<T> {
while (cur >= iterable.iteratorResults.length) {
iterable.iteratorResults.push(iterable.iterator.next());
}
const ret: IteratorResult<T> = iterable.iteratorResults[cur];
cur++;
return ret;
}
}
}
private iterator: Iterator<T>;
private iteratorResults: Array<IteratorResult<T>>;
constructor(iterable: Iterable<T>) {
this.iterator = iterable[Symbol.iterator]();
this.iteratorResults = [];
}
}
想法是传入的迭代器只用于获取一个迭代器,处理该迭代器的结果存储在一个数组中。 ReplayableIterable
分发的迭代器仅在耗尽数组内容时才咨询实际的迭代器。这是 lazily 复制迭代器:如果传入的可迭代对象有十亿个元素(或更糟,无限个),但您只打算遍历其中的几个,则不必复制您永远不会使用的元素会占用内存和时间(对于永不终止的生成器来说,时间占用确实相当可观)。
最后,导出一个将任何 Iterable
转换为 MultipleIterable
的函数,方法是返回原样的参数(如果你给它一个 MultipleIterable
开始)或以此为基础构建ReplayableIterable
export function toMultipleIterable<T>(iterable: Iterable<T>): MultipleIterable<T> {
return isMultableIterable(iterable) ? iterable : new ReplayableIterable(iterable);
}
希望对您有所帮助;祝你好运!
也许这是要走的路:
type FIterable<T> = (() => Iterable<T>) | Iterable<T>;
export function* run<T = any>(...its: FIterable<T>[]) {
while (true) {
for (const it of its) {
if (typeof it === 'function') {
yield* it(); // this throw a type error but compile :)
} else {
yield* it;
}
}
}
}
// Example:
export const map: Map<number, number> = new Map([[1, 2], [3, 4]]);
const a = run(() => map.keys(), [5, 6, 7], new Set([8, 9]));
for (const _ of Array(10)) {
console.log(a.next());
};
JS/TS 生成器可以使用 yield*
迭代生成器。
单向迭代的问题应该用函数解决,为什么不重新创建一个迭代?
本例中只有地图不能重复
我的tsconfig.json
{
"compilerOptions": {
"module": "UMD",
"target": "es5",
"lib": [
"es2015",
"es2015.iterable",
"dom"
]
},
"files": [
"app.ts"
]
}