sequence.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. */
  5. "use strict";
  6. module.metadata = {
  7. "stability": "experimental"
  8. };
  9. // Disclamer:
  10. // In this module we'll have some common argument / variable names
  11. // to hint their type or behavior.
  12. //
  13. // - `f` stands for "function" that is intended to be side effect
  14. // free.
  15. // - `p` stands for "predicate" that is function which returns logical
  16. // true or false and is intended to be side effect free.
  17. // - `x` / `y` single item of the sequence.
  18. // - `xs` / `ys` sequence of `x` / `y` items where `x` / `y` signifies
  19. // type of the items in sequence, so sequence is not of the same item.
  20. // - `_` used for argument(s) or variable(s) who's values are ignored.
  21. const { complement, flip, identity } = require("../lang/functional");
  22. const { iteratorSymbol } = require("../util/iteration");
  23. const { isArray, isArguments, isMap, isSet,
  24. isString, isBoolean, isNumber } = require("../lang/type");
  25. const Sequence = function Sequence(iterator) {
  26. if (iterator.isGenerator && iterator.isGenerator())
  27. this[iteratorSymbol] = iterator;
  28. else
  29. throw TypeError("Expected generator argument");
  30. };
  31. exports.Sequence = Sequence;
  32. const polymorphic = dispatch => x =>
  33. x === null ? dispatch.null(null) :
  34. x === void(0) ? dispatch.void(void(0)) :
  35. isArray(x) ? (dispatch.array || dispatch.indexed)(x) :
  36. isString(x) ? (dispatch.string || dispatch.indexed)(x) :
  37. isArguments(x) ? (dispatch.arguments || dispatch.indexed)(x) :
  38. isMap(x) ? dispatch.map(x) :
  39. isSet(x) ? dispatch.set(x) :
  40. isNumber(x) ? dispatch.number(x) :
  41. isBoolean(x) ? dispatch.boolean(x) :
  42. dispatch.default(x);
  43. const nogen = function*() {};
  44. const empty = () => new Sequence(nogen);
  45. exports.empty = empty;
  46. const seq = polymorphic({
  47. null: empty,
  48. void: empty,
  49. array: identity,
  50. string: identity,
  51. arguments: identity,
  52. map: identity,
  53. set: identity,
  54. default: x => x instanceof Sequence ? x : new Sequence(x)
  55. });
  56. exports.seq = seq;
  57. // Function to cast seq to string.
  58. const string = (...etc) => "".concat(...etc);
  59. exports.string = string;
  60. // Function for casting seq to plain object.
  61. const object = (...pairs) => {
  62. let result = {};
  63. for (let [key, value] of pairs)
  64. result[key] = value;
  65. return result;
  66. };
  67. exports.object = object;
  68. // Takes `getEnumerator` function that returns `nsISimpleEnumerator`
  69. // and creates lazy sequence of it's items. Note that function does
  70. // not take `nsISimpleEnumerator` itslef because that would allow
  71. // single iteration, which would not be consistent with rest of the
  72. // lazy sequences.
  73. const fromEnumerator = getEnumerator => seq(function* () {
  74. const enumerator = getEnumerator();
  75. while (enumerator.hasMoreElements())
  76. yield enumerator.getNext();
  77. });
  78. exports.fromEnumerator = fromEnumerator;
  79. // Takes `object` and returns lazy sequence of own `[key, value]`
  80. // pairs (does not include inherited and non enumerable keys).
  81. const pairs = polymorphic({
  82. null: empty,
  83. void: empty,
  84. map: identity,
  85. indexed: indexed => seq(function* () {
  86. const count = indexed.length;
  87. let index = 0;
  88. while (index < count) {
  89. yield [index, indexed[index]];
  90. index = index + 1;
  91. }
  92. }),
  93. default: object => seq(function* () {
  94. for (let key of Object.keys(object))
  95. yield [key, object[key]];
  96. })
  97. });
  98. exports.pairs = pairs;
  99. const keys = polymorphic({
  100. null: empty,
  101. void: empty,
  102. indexed: indexed => seq(function* () {
  103. const count = indexed.length;
  104. let index = 0;
  105. while (index < count) {
  106. yield index;
  107. index = index + 1;
  108. }
  109. }),
  110. map: map => seq(function* () {
  111. for (let [key, _] of map)
  112. yield key;
  113. }),
  114. default: object => seq(function* () {
  115. for (let key of Object.keys(object))
  116. yield key;
  117. })
  118. });
  119. exports.keys = keys;
  120. const values = polymorphic({
  121. null: empty,
  122. void: empty,
  123. set: identity,
  124. indexed: indexed => seq(function* () {
  125. const count = indexed.length;
  126. let index = 0;
  127. while (index < count) {
  128. yield indexed[index];
  129. index = index + 1;
  130. }
  131. }),
  132. map: map => seq(function* () {
  133. for (let [_, value] of map) yield value;
  134. }),
  135. default: object => seq(function* () {
  136. for (let key of Object.keys(object)) yield object[key];
  137. })
  138. });
  139. exports.values = values;
  140. // Returns a lazy sequence of `x`, `f(x)`, `f(f(x))` etc.
  141. // `f` must be free of side-effects. Note that returned
  142. // sequence is infinite so it must be consumed partially.
  143. //
  144. // Implements clojure iterate:
  145. // http://clojuredocs.org/clojure_core/clojure.core/iterate
  146. const iterate = (f, x) => seq(function* () {
  147. let state = x;
  148. while (true) {
  149. yield state;
  150. state = f(state);
  151. }
  152. });
  153. exports.iterate = iterate;
  154. // Returns a lazy sequence of the items in sequence for which `p(item)`
  155. // returns `true`. `p` must be free of side-effects.
  156. //
  157. // Implements clojure filter:
  158. // http://clojuredocs.org/clojure_core/clojure.core/filter
  159. const filter = (p, sequence) => seq(function* () {
  160. if (sequence !== null && sequence !== void(0)) {
  161. for (let item of sequence) {
  162. if (p(item))
  163. yield item;
  164. }
  165. }
  166. });
  167. exports.filter = filter;
  168. // Returns a lazy sequence consisting of the result of applying `f` to the
  169. // set of first items of each sequence, followed by applying f to the set
  170. // of second items in each sequence, until any one of the sequences is
  171. // exhausted. Any remaining items in other sequences are ignored. Function
  172. // `f` should accept number-of-sequences arguments.
  173. //
  174. // Implements clojure map:
  175. // http://clojuredocs.org/clojure_core/clojure.core/map
  176. const map = (f, ...sequences) => seq(function* () {
  177. const count = sequences.length;
  178. // Optimize a single sequence case
  179. if (count === 1) {
  180. let [sequence] = sequences;
  181. if (sequence !== null && sequence !== void(0)) {
  182. for (let item of sequence)
  183. yield f(item);
  184. }
  185. }
  186. else {
  187. // define args array that will be recycled on each
  188. // step to aggregate arguments to be passed to `f`.
  189. let args = [];
  190. // define inputs to contain started generators.
  191. let inputs = [];
  192. let index = 0;
  193. while (index < count) {
  194. inputs[index] = sequences[index][iteratorSymbol]();
  195. index = index + 1;
  196. }
  197. // Run loop yielding of applying `f` to the set of
  198. // items at each step until one of the `inputs` is
  199. // exhausted.
  200. let done = false;
  201. while (!done) {
  202. let index = 0;
  203. let value = void(0);
  204. while (index < count && !done) {
  205. ({ done, value }) = inputs[index].next();
  206. // If input is not exhausted yet store value in args.
  207. if (!done) {
  208. args[index] = value;
  209. index = index + 1;
  210. }
  211. }
  212. // If none of the inputs is exhasted yet, `args` contain items
  213. // from each input so we yield application of `f` over them.
  214. if (!done)
  215. yield f(...args);
  216. }
  217. }
  218. });
  219. exports.map = map;
  220. // Returns a lazy sequence of the intermediate values of the reduction (as
  221. // per reduce) of sequence by `f`, starting with `initial` value if provided.
  222. //
  223. // Implements clojure reductions:
  224. // http://clojuredocs.org/clojure_core/clojure.core/reductions
  225. const reductions = (...params) => {
  226. const count = params.length;
  227. let hasInitial = false;
  228. let f, initial, source;
  229. if (count === 2) {
  230. ([f, source]) = params;
  231. }
  232. else if (count === 3) {
  233. ([f, initial, source]) = params;
  234. hasInitial = true;
  235. }
  236. else {
  237. throw Error("Invoked with wrong number of arguments: " + count);
  238. }
  239. const sequence = seq(source);
  240. return seq(function* () {
  241. let started = hasInitial;
  242. let result = void(0);
  243. // If initial is present yield it.
  244. if (hasInitial)
  245. yield (result = initial);
  246. // For each item of the sequence accumulate new result.
  247. for (let item of sequence) {
  248. // If nothing has being yield yet set result to first
  249. // item and yield it.
  250. if (!started) {
  251. started = true;
  252. yield (result = item);
  253. }
  254. // Otherwise accumulate new result and yield it.
  255. else {
  256. yield (result = f(result, item));
  257. }
  258. }
  259. // If nothing has being yield yet it's empty sequence and no
  260. // `initial` was provided in which case we need to yield `f()`.
  261. if (!started)
  262. yield f();
  263. });
  264. };
  265. exports.reductions = reductions;
  266. // `f` should be a function of 2 arguments. If `initial` is not supplied,
  267. // returns the result of applying `f` to the first 2 items in sequence, then
  268. // applying `f` to that result and the 3rd item, etc. If sequence contains no
  269. // items, `f` must accept no arguments as well, and reduce returns the
  270. // result of calling f with no arguments. If sequence has only 1 item, it
  271. // is returned and `f` is not called. If `initial` is supplied, returns the
  272. // result of applying `f` to `initial` and the first item in sequence, then
  273. // applying `f` to that result and the 2nd item, etc. If sequence contains no
  274. // items, returns `initial` and `f` is not called.
  275. //
  276. // Implements clojure reduce:
  277. // http://clojuredocs.org/clojure_core/clojure.core/reduce
  278. const reduce = (...args) => {
  279. const xs = reductions(...args);
  280. let x;
  281. for (x of xs) void(0);
  282. return x;
  283. };
  284. exports.reduce = reduce;
  285. const each = (f, sequence) => {
  286. for (let x of seq(sequence)) void(f(x));
  287. };
  288. exports.each = each;
  289. const inc = x => x + 1;
  290. // Returns the number of items in the sequence. `count(null)` && `count()`
  291. // returns `0`. Also works on strings, arrays, Maps & Sets.
  292. // Implements clojure count:
  293. // http://clojuredocs.org/clojure_core/clojure.core/count
  294. const count = polymorphic({
  295. null: _ => 0,
  296. void: _ => 0,
  297. indexed: indexed => indexed.length,
  298. map: map => map.size,
  299. set: set => set.size,
  300. default: xs => reduce(inc, 0, xs)
  301. });
  302. exports.count = count;
  303. // Returns `true` if sequence has no items.
  304. // Implements clojure empty?:
  305. // http://clojuredocs.org/clojure_core/clojure.core/empty_q
  306. const isEmpty = sequence => {
  307. // Treat `null` and `undefined` as empty sequences.
  308. if (sequence === null || sequence === void(0))
  309. return true;
  310. // If contains any item non empty so return `false`.
  311. for (let _ of sequence)
  312. return false;
  313. // If has not returned yet, there was nothing to iterate
  314. // so it's empty.
  315. return true;
  316. };
  317. exports.isEmpty = isEmpty;
  318. const and = (a, b) => a && b;
  319. // Returns true if `p(x)` is logical `true` for every `x` in sequence, else
  320. // `false`.
  321. //
  322. // Implements clojure every?:
  323. // http://clojuredocs.org/clojure_core/clojure.core/every_q
  324. const isEvery = (p, sequence) => {
  325. if (sequence !== null && sequence !== void(0)) {
  326. for (let item of sequence) {
  327. if (!p(item))
  328. return false;
  329. }
  330. }
  331. return true;
  332. };
  333. exports.isEvery = isEvery;
  334. // Returns the first logical true value of (p x) for any x in sequence,
  335. // else `null`.
  336. //
  337. // Implements clojure some:
  338. // http://clojuredocs.org/clojure_core/clojure.core/some
  339. const some = (p, sequence) => {
  340. if (sequence !== null && sequence !== void(0)) {
  341. for (let item of sequence) {
  342. if (p(item))
  343. return true;
  344. }
  345. }
  346. return null;
  347. };
  348. exports.some = some;
  349. // Returns a lazy sequence of the first `n` items in sequence, or all items if
  350. // there are fewer than `n`.
  351. //
  352. // Implements clojure take:
  353. // http://clojuredocs.org/clojure_core/clojure.core/take
  354. const take = (n, sequence) => n <= 0 ? empty() : seq(function* () {
  355. let count = n;
  356. for (let item of sequence) {
  357. yield item;
  358. count = count - 1;
  359. if (count === 0) break;
  360. }
  361. });
  362. exports.take = take;
  363. // Returns a lazy sequence of successive items from sequence while
  364. // `p(item)` returns `true`. `p` must be free of side-effects.
  365. //
  366. // Implements clojure take-while:
  367. // http://clojuredocs.org/clojure_core/clojure.core/take-while
  368. const takeWhile = (p, sequence) => seq(function* () {
  369. for (let item of sequence) {
  370. if (!p(item))
  371. break;
  372. yield item;
  373. }
  374. });
  375. exports.takeWhile = takeWhile;
  376. // Returns a lazy sequence of all but the first `n` items in
  377. // sequence.
  378. //
  379. // Implements clojure drop:
  380. // http://clojuredocs.org/clojure_core/clojure.core/drop
  381. const drop = (n, sequence) => seq(function* () {
  382. if (sequence !== null && sequence !== void(0)) {
  383. let count = n;
  384. for (let item of sequence) {
  385. if (count > 0)
  386. count = count - 1;
  387. else
  388. yield item;
  389. }
  390. }
  391. });
  392. exports.drop = drop;
  393. // Returns a lazy sequence of the items in sequence starting from the
  394. // first item for which `p(item)` returns falsy value.
  395. //
  396. // Implements clojure drop-while:
  397. // http://clojuredocs.org/clojure_core/clojure.core/drop-while
  398. const dropWhile = (p, sequence) => seq(function* () {
  399. let keep = false;
  400. for (let item of sequence) {
  401. keep = keep || !p(item);
  402. if (keep) yield item;
  403. }
  404. });
  405. exports.dropWhile = dropWhile;
  406. // Returns a lazy sequence representing the concatenation of the
  407. // suplied sequences.
  408. //
  409. // Implements clojure conact:
  410. // http://clojuredocs.org/clojure_core/clojure.core/concat
  411. const concat = (...sequences) => seq(function* () {
  412. for (let sequence of sequences)
  413. for (let item of sequence)
  414. yield item;
  415. });
  416. exports.concat = concat;
  417. // Returns the first item in the sequence.
  418. //
  419. // Implements clojure first:
  420. // http://clojuredocs.org/clojure_core/clojure.core/first
  421. const first = sequence => {
  422. if (sequence !== null && sequence !== void(0)) {
  423. for (let item of sequence)
  424. return item;
  425. }
  426. return null;
  427. };
  428. exports.first = first;
  429. // Returns a possibly empty sequence of the items after the first.
  430. //
  431. // Implements clojure rest:
  432. // http://clojuredocs.org/clojure_core/clojure.core/rest
  433. const rest = sequence => drop(1, sequence);
  434. exports.rest = rest;
  435. // Returns the value at the index. Returns `notFound` or `undefined`
  436. // if index is out of bounds.
  437. const nth = (xs, n, notFound) => {
  438. if (n >= 0) {
  439. if (isArray(xs) || isArguments(xs) || isString(xs)) {
  440. return n < xs.length ? xs[n] : notFound;
  441. }
  442. else if (xs !== null && xs !== void(0)) {
  443. let count = n;
  444. for (let x of xs) {
  445. if (count <= 0)
  446. return x;
  447. count = count - 1;
  448. }
  449. }
  450. }
  451. return notFound;
  452. };
  453. exports.nth = nth;
  454. // Return the last item in sequence, in linear time.
  455. // If `sequence` is an array or string or arguments
  456. // returns in constant time.
  457. // Implements clojure last:
  458. // http://clojuredocs.org/clojure_core/clojure.core/last
  459. const last = polymorphic({
  460. null: _ => null,
  461. void: _ => null,
  462. indexed: indexed => indexed[indexed.length - 1],
  463. map: xs => reduce((_, x) => x, xs),
  464. set: xs => reduce((_, x) => x, xs),
  465. default: xs => reduce((_, x) => x, xs)
  466. });
  467. exports.last = last;
  468. // Return a lazy sequence of all but the last `n` (default 1) items
  469. // from the give `xs`.
  470. //
  471. // Implements clojure drop-last:
  472. // http://clojuredocs.org/clojure_core/clojure.core/drop-last
  473. const dropLast = flip((xs, n=1) => seq(function* () {
  474. let ys = [];
  475. for (let x of xs) {
  476. ys.push(x);
  477. if (ys.length > n)
  478. yield ys.shift();
  479. }
  480. }));
  481. exports.dropLast = dropLast;
  482. // Returns a lazy sequence of the elements of `xs` with duplicates
  483. // removed
  484. //
  485. // Implements clojure distinct
  486. // http://clojuredocs.org/clojure_core/clojure.core/distinct
  487. const distinct = sequence => seq(function* () {
  488. let items = new Set();
  489. for (let item of sequence) {
  490. if (!items.has(item)) {
  491. items.add(item);
  492. yield item;
  493. }
  494. }
  495. });
  496. exports.distinct = distinct;
  497. // Returns a lazy sequence of the items in `xs` for which
  498. // `p(x)` returns false. `p` must be free of side-effects.
  499. //
  500. // Implements clojure remove
  501. // http://clojuredocs.org/clojure_core/clojure.core/remove
  502. const remove = (p, xs) => filter(complement(p), xs);
  503. exports.remove = remove;
  504. // Returns the result of applying concat to the result of
  505. // `map(f, xs)`. Thus function `f` should return a sequence.
  506. //
  507. // Implements clojure mapcat
  508. // http://clojuredocs.org/clojure_core/clojure.core/mapcat
  509. const mapcat = (f, sequence) => seq(function* () {
  510. const sequences = map(f, sequence);
  511. for (let sequence of sequences)
  512. for (let item of sequence)
  513. yield item;
  514. });
  515. exports.mapcat = mapcat;