JavaScript中的范围式迭代器

3
我希望提高我的JavaScript函数式编程技能。作为练习,我想将H.S.Teoh著名的无循环日历打印程序移植到JS中-在原始D实现中解释在此处https://wiki.dlang.org/Component_programming_with_ranges,以及在Eric Niebler的精彩YouTube讲座中C++端口https://youtu.be/mFUXNMfaciE
我尝试使用JavaScript的本地迭代器和生成器https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators,但很快就遇到了困难。我认为这些不足以完成此任务。特别是:
1.它们不允许对迭代器的已完成状态进行非破坏性查询, 2.没有机制可以克隆迭代器。
我快速查看了RxJshttp://reactivex.io/rxjs/,并打算将其用于此目的。我担心它需要大量的学习,并且过于庞大,而且并没有真正设计来解决我所面临的问题(这不是一个异步问题等),而且我也不知道它是否可行。
我的问题是:
1.RxJs是JS中模拟范围样式迭代器的合理方法吗? 2.如果上述问题的答案是否定的,那么更好的库或方法是什么?

2
RxJs是在JS中模拟范围迭代器的合理方法吗?不,这不是一个合理的方法。为什么你认为你需要这些功能中的任何一个呢? - Aluan Haddad
你为什么认为你需要这些功能之一?正如我在第一个引用中所描述的那样,这是日历程序所需的。 - willw
没有必要直接将D解决方案空间映射到JavaScript解决方案空间。此外,在JavaScript中“克隆”迭代器很简单:var other = [...iterator] - Aluan Haddad
无需直接将D解决方案空间映射到JavaScript解决方案空间。不,但我希望模仿原始效果(编写日历程序而不使用ifs,冗余缓冲区的循环)。毕竟,已经在C++中完成了这项工作。在JavaScript中“克隆”迭代器很简单。如果被迭代的东西不是数组,那么这个方法是否可行?我从这个引用https://esdiscuss.org/topic/how-would-we-copy-an-iterator 得到的印象是不支持的。 - willw
1个回答

2
JavaScript支持将函数作为一等数据,因此您可以轻松地使用抽象来创建自己的工具。在下面的示例中,我们使用我们自己创造的抽象YieldReturn来发明我们自己的持久化(不可变)迭代器。使用记忆化技术可以避免重复计算迭代器的下一个值。
我们的持久化迭代器几乎与JS本机迭代器表现相同,只是它们是不可变的!donevaluenext属性应该感觉很熟悉。

const Memo = (f, memo) => () =>
  memo === undefined
    ? (memo = f (), memo)
    : memo

const Yield = (value, next = Return) =>
  ({ done: false, value, next: Memo (next) })
  
const Return = value =>
  ({ done: true, value })

const Range = (min = 0, max = Infinity) =>
  min > max
    ? Return ()
    : Yield (min, () => Range (min + 1, max))

const state0 =
  Range (0, 2)
  
console.log (state0.done)  // false
console.log (state0.value) // 0  
console.log (state0.value) // 0

const state1 =
  state0.next ()

console.log (state1.done)  // false
console.log (state1.value) // 1
console.log (state1.value) // 1

const state2 =
  state1.next ()

console.log (state2.done)  // false
console.log (state2.value) // 2
console.log (state2.value) // 2

const state3 =
  state2.next ()

console.log (state3.done) // true

但是你不应该使用赋值来遍历迭代器,递归才是我们需要的

const MappedIterator = (f, it = Return ()) =>
  it.done
    ? Return ()
    : Yield (f (it.value), () => MappedIterator (f, it.next ()))

const Generator = function* (it = Return ())
{
  while (it.done === false)
    (yield it.value, it = it.next ())
  return it.value
}

const square = x =>
  x * x

Array.from (Generator (MappedIterator (square, Range (0, 10))))
// => [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 ]

当然,由于迭代器是持久的,我们可以多次遍历同一迭代器。
const ConcatIterator = (x = Return (), y = Return ()) =>
  x.done
    ? y
    : Yield (x.value, () => ConcatIterator (x.next (), y))

const it =
  MappedIterator (square, Range (1, 3))

Array.from (Generator (it))                      // => [ 1, 4, 9 ]
Array.from (Generator (ConcatIterator (it, it))) // => [ 1, 4, 9, 1, 4, 9 ]

这是完整的代码演示。

const Memo = (f, memo) => () =>
  memo === undefined
    ? (memo = f (), memo)
    : memo

const Yield = (value, next = Return) =>
  ({ done: false, value, next: Memo (next) })
  
const Return = value =>
  ({ done: true, value })

const Range = (min = 0, max = Infinity) =>
  min > max
    ? Return ()
    : Yield (min, () => Range (min + 1, max))

const MappedIterator = (f, it = Return ()) =>
  it.done
    ? Return ()
    : Yield (f (it.value), () => MappedIterator (f, it.next ()))

const ConcatIterator = (x = Return (), y = Return ()) =>
  x.done
    ? y
    : Yield (x.value, () => ConcatIterator (x.next (), y))
    
const Generator = function* (it = Return ())
{
  while (it.done === false)
    (yield it.value, it = it.next ())
  return it.value
}

const it =
  MappedIterator (x => x * x, Range (1, 3))

console.log (Array.from (Generator (it)))
// [ 1, 4, 9 ]

console.log (Array.from (Generator (ConcatIterator (it, it))))
// [ 1, 4, 9, 1, 4, 9 ]


@Bergi从你口中听到这样的赞美,我会把它当作无比的恭维 ^_^ - Mulan
2
仔细思考后,我想我也能够辨认出代码的格式。你有一个非常独特的布局(你可以把它当作是赞美 :-P)。 - Bergi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接