JavaScript

时间:2018-01-11 08:32:48

标签: javascript functional-programming d

我希望提高我的JavaScript函数编程技巧。作为练习,我想移植到JS HS Teoh着名的无环日历打印输出程序 - 在其原始D实现中解释https://wiki.dlang.org/Component_programming_with_ranges,以及由Eric Niebler在C ++端口进行的精彩YouTube演讲https://youtu.be/mFUXNMfaciE

我尝试过使用JavaScript的原生迭代器和生成器https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators,并且很快就会崩溃。我觉得这些不足以完成这项任务。特别是:

  1. 他们不允许对迭代器进行非破坏性查询     完成状态,
  2. 没有克隆迭代器的机制。
  3. 我已经快速浏览了RxJs http://reactivex.io/rxjs/,以便将其用于此目的。我担心这需要大量的学习并且是非常难以理解的,并不是真的旨在解决我所遇到的问题(这不是异步问题等),而且我所知道的可能都不起作用。

    我的问题是:

    1. RxJs是一种在JS中模拟范围式迭代器的合理方法吗?
    2. 如果上面的答案是“否”,那么图书馆或方法会更好吗?

1 个答案:

答案 0 :(得分:3)

JavaScript支持函数作为一流数据,因此您可以轻松地抽象自己的玩具。下面我们使用我们制作的抽象<head> <base href="https://polygit.org/polymer+v2.3.1/components/"> <script src="webcomponentsjs/webcomponents-loader.js"></script> <link rel="import" href="polymer/polymer.html"> <link rel="import" href="iron-icons/iron-icons.html"> <link rel="import" href="iron-icon/iron-icon.html"> </head> <body> <x-foo></x-foo> <dom-module id="x-foo"> <template> <style> x-suggestions { width: 350px; } </style> <x-suggestions suggestions="[[suggestions]]"></x-suggestions> </template> </dom-module> <dom-module id="x-suggestions"> <template> <style> :host { display: block; background: #f8f8f8; color: #6a6a6a; padding-bottom: 1rem; } .title { margin: 0; padding: 1rem 1rem 1rem 1.3rem; } .feedback { text-decoration: none; font-style: italic; color: #6a6a6a; margin: 1rem; } .suggestion { text-decoration: none; font-weight: bold; color: black; } .suggestions-outer { overflow-x: auto; } .suggestions-inner { white-space: nowrap; } .suggestion-box { display: inline-flex; margin: 0.2rem; padding: 1em 1em 1em 0.5em; border: solid 1px #ddd; border-radius: 2px; } .header { display: flex; border-top: solid 2px #e9e9e9; } .close-btn { background: transparent; border: none; margin: 10px 10px 10px auto; font-size: 1rem; color: #6a6a6a; cursor: pointer; } .icon-search { color: #717171; --iron-icon-height: 28px; --iron-icon-width: 28px; } </style> <header class="header"> <h3 class="title">People also search for</h3> <button class="close-btn" title$="Close" on-click="_onClickClose">✕</button> </header> <div class="suggestions-outer"> <div class="suggestions-inner"> <template is="dom-repeat" items="[[suggestions]]"> <div class="suggestion-box"> <a class="suggestion" target$="_blank" href$="[[item.url]]"> <iron-icon class="icon-search" icon="search"></iron-icon> <span>[[item.title]]</span> </a> </div> </template> <a href="#" class="feedback" on-click="_onClickFeedback">Feedback</a> </div> </div> </template> </dom-module> </body>Yield来创建我们自己的persistent(不可变)迭代器。 Memoization用于避免重复计算迭代器的下一个值。

我们的持久迭代器的行为几乎与它们的原生JS对应物一样,只有它们是不可变的! Returndonevalue属性应该让您感到熟悉。

next

但是你不打算通过使用赋值迭代迭代器,递归正是我们在这里需要的

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 ]