Skip to content

Commit

Permalink
sequence: add the terminal count$ operation
Browse files Browse the repository at this point in the history
  • Loading branch information
Dierk Koenig committed Dec 13, 2024
1 parent 2674d9b commit fbbc91b
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 26 deletions.
2 changes: 1 addition & 1 deletion docs/src/allTests.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<h1>All Tests Report</h1>
<div id="out"></div>

<p> 1385 test(s) expected.</p>
<p> 1398 test(s) expected.</p>
<p id="grossTotal">Check console for possible errors.</p>

<footer></footer>
Expand Down
3 changes: 1 addition & 2 deletions docs/src/examples/sequence/Collatz.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ <h1>Collatz</h1>
import { Seq } from "../../kolibri/sequence/sequence.js";
import { memoize } from "../../kolibri/util/memoize.js";
import { dom, select } from "../../kolibri/util/dom.js";
import { count$ } from "../../kolibri/sequence/util/helpers.js";

const collatzSeq = n =>
n < 2
Expand All @@ -74,7 +73,7 @@ <h1>Collatz</h1>
<table>
<tr>
<td>${n}</td>
<td>${count$(seq)}</td>
<td>${seq.count$()}</td>
<td>${seq.max$()}</td>
<td>${seq.show(10_000)}</td>
</tr>
Expand Down
5 changes: 5 additions & 0 deletions docs/src/kolibri/sequence/sequencePrototype.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
zipWith
} from "./operators/operators.js";
import {
count$,
eq$,
foldr$,
foldl$,
Expand Down Expand Up @@ -108,6 +109,10 @@ SequencePrototype.toString = function (maxValues = 50) {
return show(this, maxValues);
};

SequencePrototype.count$ = function() {
return count$(this);
};

SequencePrototype.eq$ = function(that) {
if (!isIterable(that)) return false;
return eq$(this) /* == */ (that);
Expand Down
10 changes: 7 additions & 3 deletions docs/src/kolibri/sequence/sequencePrototypeTest.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TestSuite, withAppender} from "../util/test.js";
import { drop, forever, map, nil, Range, reduce$, Seq, Sequence, show, take } from "./sequence.js";
import { Just, Nothing } from "../stdlib.js";
import { TestSuite, withAppender} from "../util/test.js";
import {drop, forever, map, nil, Range, reduce$, Seq, Sequence, show, take, Walk} from "./sequence.js";
import { Just, Nothing } from "../stdlib.js";
import { Appender as ArrayAppender } from "../logger/appender/arrayAppender.js";
import { LOG_WARN } from "../logger/logLevel.js";
import { LOG_CONTEXT_KOLIBRI_SEQUENCE } from "./sequencePrototype.js";
Expand Down Expand Up @@ -46,6 +46,10 @@ testSuite.add("test prototype: toString, show", assert => {
});
});

testSuite.add("test prototype: count$", assert => {
assert.is(Walk(0).count$(), 1);
});

testSuite.add("test prototype: [==], eq$", assert => {
assert.isTrue(Range(0, 3).eq$ (Range(0, 3)));
assert.isTrue(Range(0, 3) ["=="] (Range(0, 3)));
Expand Down
5 changes: 5 additions & 0 deletions docs/src/kolibri/sequence/sequenceTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@
* Collection of all terminal operations that are defined on a {@link SequenceType}.
* @template _T_
* @typedef SequenceTerminalOperationTypes
* @property { CountSequenceOperationType } count$
* - Type: {@link CountSequenceOperationType}
* - Count the number of elements
* - **Warning**: This only works on finite sequences
* - Example: `Seq(1, 2).count$() === 2`
* @property { EqualOperationType<_T_> } "=="
* - Type: {@link EqualOperationType}
* - Check for element-wise equality
Expand Down
40 changes: 40 additions & 0 deletions docs/src/kolibri/sequence/terminalOperations/count/count.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { reduce$ } from "../reduce/reduce.js";

export { count$ }

/**
* @typedef CountSequenceOperationType
* @type { () => CountOperationType }
*/
/**
* Count the number of elements in a **finite** {@link Iterable}.
* The **finite** constraint is indicated by the trailing **$** in the function name.
*
* _Note_:
* When erroneously called on an **infinite** iterable, the function **does not return**.
* So better be safe and first {@link TakeOperationType take} as many elements as you consider the allowed maximum count.
*
*
* @typedef CountOperationType
* @function
* @pure
* @haskell [a] -> Int
* @param { Iterable } iterable - a finite iterable
* @returns { Number }
*
* @example
* const numbers = [1, 3, 0, 5];
* const count = count$(numbers);
*
* console.log(count);
* // => Logs '4'
*
* const infinite = Sequence(0, forever, id); // this cannot be counted
* assert.is(count$( take(10)(infinite) ), 10); // take with upper limit
*/

/**
* see {@link CountOperationType}
* @type { CountOperationType }
*/
const count$ = iterable => /** @type { Number } */ reduce$( (acc, _cur) => acc + 1, 0)(iterable);
36 changes: 36 additions & 0 deletions docs/src/kolibri/sequence/terminalOperations/count/countTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { addToTestingTable, TESTS } from "../../util/testingTable.js";
import { TestSuite } from "../../../util/test.js";
import {nil, append, forever, Sequence, take} from "../../sequence.js";
import { createTestConfig } from "../../util/testUtil.js";
import { count$ } from "./count.js";
import {id} from "../../../stdlib.js";

const testSuite = TestSuite("Sequence: terminal operation count$");

addToTestingTable(testSuite)(
createTestConfig({
name: "count$",
iterable: () => nil,
operation: () => count$,
evalFn: expected => actual => expected === actual,
expected: 0,
excludedTests: [TESTS.TEST_CB_NOT_CALLED_AFTER_DONE],
invariants: [
it => count$(it) * 2 === count$(append(it)(it)),
]
})
);

testSuite.add("zero, one, many", assert => {
assert.is(count$(nil), 0);
assert.is(count$([42]), 1);
assert.is(count$(Array.from({length: 1000})), 1000);
});

testSuite.add("take and then count", assert => {
const infinite = Sequence(0, forever, id); // this cannot be counted
assert.is(count$( take(10)(infinite) ), 10); // take with upper limit
assert.is(count$( take(10)( nil) ), 0); // upper limit does not get in the way if iterable is shorter
});

testSuite.run();
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./count/count.js";
export * from "./eq/eq.js";
export * from "./foldr/foldr.js";
export * from "./forEach/forEach.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "./count/countTest.js"
import "./eq/eqTest.js"
import "./foldr/foldrTest.js";
import "./forEach/forEachTest.js";
Expand Down
9 changes: 1 addition & 8 deletions docs/src/kolibri/sequence/util/helperTest.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { TestSuite } from "../../util/test.js";
import { Seq } from "../constructors/seq/seq.js";
import { Pair } from "../../lambda/pair.js";
import { count$, forever, isIterable, isSequence, plus, toSeq,limit } from "./helpers.js";
import { Walk } from "../constructors/range/range.js";
import { forever, isIterable, isSequence, plus, toSeq,limit } from "./helpers.js";
import { Sequence } from "../constructors/sequence/Sequence.js";

const testSuite = TestSuite("Sequence: helper");
Expand Down Expand Up @@ -49,12 +48,6 @@ testSuite.add("plus with numbers", assert => {
assert.is(result, 6 );
});

testSuite.add("count$", assert => {
assert.is(count$(Seq()), 0 );
assert.is(count$(Seq("x")), 1 );
assert.is(count$(Walk(1,1000)), 1000 );
});

testSuite.add("limit ok", assert => {
const halves = Sequence(1, forever, n => n/2);
assert.is(limit(1/2, halves), 1/2 );
Expand Down
10 changes: 1 addition & 9 deletions docs/src/kolibri/sequence/util/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { map } from "../operators/map/map.js"
import { id } from "../../stdlib.js"
import { SequencePrototype } from "../sequencePrototype.js";

export { toSeq, isIterable, iteratorOf, ensureSequence, isSequence, forever, plus, count$, limit }
export { toSeq, isIterable, iteratorOf, ensureSequence, isSequence, forever, plus, limit }

/**
* Casts an arbitrary {@link Iterable} into the {@link SequenceType}.
Expand Down Expand Up @@ -73,14 +73,6 @@ const forever = _ => true;
*/
const plus = (acc, cur) => acc + cur;

/**
* Convenience function to count the number of elements in a {@link SequenceType sequence}.
* @template _T_
* @param { SequenceType<_T_> } sequence - must be finite as indicated by the trailing "$"
* @return { Number } zero or positive integer number
*/
const count$ = sequence => sequence.foldl$( (acc, _cur) => ++acc, 0);

/**
* Calculate the limit that the number sequence approaches by comparing successive elements until they are
* less than epsilon apart.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/kolibri/util/domTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { TestSuite } from "./test.js";
import { withDebugTestArrayAppender } from "../logger/loggerTest.js";
import { setLoggingLevel, setLoggingContext } from "../logger/logging.js";
import { LOG_WARN } from "../logger/logLevel.js";
import { count$ } from "../sequence/util/helpers.js";
import { dom, fireChangeEvent, select } from "./dom.js";
import { count$ } from "../sequence/terminalOperations/count/count.js";

const domSuite = TestSuite("util/dom");

Expand Down
4 changes: 2 additions & 2 deletions docs/src/kolibri/version.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export { release, dateStamp, versionInfo, clientId }

const release = "0.9.5";
const release = "0.9.6";

const dateStamp = "2024-12-08 T 16:12:25 MEZ";
const dateStamp = "2024-12-13 T 17:51:07 MEZ";

const versionInfo = release + " at " + dateStamp;

Expand Down

0 comments on commit fbbc91b

Please sign in to comment.