javascript - JavaScript 中的范围式迭代器

标签 javascript functional-programming d

我希望提高我的 JavaScript 函数式编程技能。作为练习,我想移植到 JS H. S. Teoh 著名的无环日历打印输出程序 - 此处解释 https://wiki.dlang.org/Component_programming_with_ranges在其最初的 D 实现中,以及在一个很好的 YouTube 讲座中 https://youtu.be/mFUXNMfaciE由 Eric Niebler 在 C++ 端口中编写。

我尝试过使用 JavaScript 的原生迭代器和生成器 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators , 并迅速解开。我的印象是这些不足以完成这项任务。特别是:

  1. 他们不允许对迭代器的非破坏性查询 完成状态,
  2. 没有克隆迭代器的机制。

我快速浏览了 RxJs http://reactivex.io/rxjs/以期将其用于此目的。我担心这需要大量的学习,而且过于矫枉过正,并不是真正设计来解决我遇到的问题(这不是异步问题等),据我所知可能行不通。

我的问题是:

  1. RxJs 是在 JS 中模拟范围式迭代器的合理方法吗?
  2. 如果对上述 1. 的回答是“否”,什么是更好的库或方法?

最佳答案

JavaScript 支持将函数作为一等数据,因此您可以轻松地抽象出自己的玩具。下面我们自己发明persistent (不可变的)迭代器使用我们制作的抽象,YieldReturnMemoization用于避免重复计算迭代器的下一个值。

我们的持久化迭代器的行为几乎与它们的原生 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 ]

关于javascript - JavaScript 中的范围式迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48202696/

相关文章:

javascript - 在 JS 文本区域中将大小写更改为小写

从输入地址范围(如 1-12A)创建地址字母​​/数字列表的 Pythonic 方法

scala - 有效地搜索序列序列中的单个第一个匹配

D 结构体内存处理 - 从成员函数返回 `this`

constants - D:为什么 opIndex 在 std.container.Array 类中不是 const 限定的?

arrays - 关联数组不存储映射

javascript - React - 如何创建链式级联辅助函数

javascript - 基于 Dropdown 的 React Effector 状态管理

javascript - Adobe CQ/AEM - 触发编辑后处理程序

javascript - 为什么当所有函数都是 future 时,第三个函数不会在 pipeK 中被调用?