Skip to content

Commit 7b49232

Browse files
🚧 progress: First draft.
1 parent e04b7c8 commit 7b49232

40 files changed

+9645
-13
lines changed

doc/scripts/header.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ domReady(() => {
1717
header.insertBefore(projectname, header.firstChild);
1818

1919
const testlink = document.querySelector('header > a[data-ice="testLink"]');
20-
testlink.href = 'https://app.codecov.io/gh/data-structure-algebra/singly-linked-list';
20+
testlink.href =
21+
'https://app.codecov.io/gh/data-structure-algebra/singly-linked-list';
2122
testlink.target = '_BLANK';
2223

2324
const searchBox = document.querySelector('.search-box');

package.json

+6-9
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565
"@babel/preset-env": "7.15.0",
6666
"@babel/register": "7.14.5",
6767
"@commitlint/cli": "13.1.0",
68+
"@iterable-iterator/list": "^1.0.1",
69+
"@iterable-iterator/map": "^1.0.1",
70+
"@iterable-iterator/range": "^2.1.0",
6871
"@js-library/commitlint-config": "0.0.4",
6972
"ava": "3.15.0",
7073
"babel-plugin-transform-remove-console": "6.9.4",
@@ -197,15 +200,9 @@
197200
"unicorn"
198201
],
199202
"rules": {
200-
"unicorn/filename-case": [
201-
"error",
202-
{
203-
"cases": {
204-
"camelCase": true,
205-
"pascalCase": true
206-
}
207-
}
208-
],
203+
"camelcase": "off",
204+
"unicorn/prevent-abbreviations": "off",
205+
"unicorn/filename-case": "off",
209206
"unicorn/prefer-node-protocol": "off"
210207
},
211208
"overrides": [

src/Node.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Node.
3+
*
4+
* @param {any} value
5+
*/
6+
export default function Node(value) {
7+
this._value = value;
8+
this._next = null;
9+
}

src/_isLast.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isEmpty from './isEmpty.js';
6+
import isNonEmpty from './isNonEmpty.js';
7+
import _shift from './_shift.js';
8+
9+
/**
10+
* _isLast.
11+
*
12+
* @param {Node} x
13+
* @return {boolean}
14+
*/
15+
export default function _isLast(x) {
16+
assert(isNonEmpty(x));
17+
return isEmpty(_shift(x));
18+
}

src/_iter.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import _shift from './_shift.js';
6+
import isEmpty from './isEmpty.js';
7+
import isNonEmpty from './isNonEmpty.js';
8+
9+
/**
10+
* Generator of nodes in list in order. You are allowed to edit the current
11+
* node.
12+
*
13+
* /!\ Modifying the next pointer of the current node will NOT change which
14+
* node comes next in the iteration.
15+
*
16+
* @param {Node} first First node of the list.
17+
* @return {IterableIterator<Node>} Yields nodes of a list in order.
18+
*/
19+
export default function* _iter(first) {
20+
assert(isNonEmpty(first));
21+
let next = first;
22+
23+
do {
24+
const x = next;
25+
next = _shift(x); // Compute next before yielding.
26+
yield x; // Necessary ?
27+
} while (!isEmpty(next));
28+
}

src/_iter_fast.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isEmpty from './isEmpty.js';
6+
import isNonEmpty from './isNonEmpty.js';
7+
import _shift from './_shift.js';
8+
9+
/**
10+
* Generator of nodes in list in order. The list cannot be empty. You should
11+
* not modify the current node's next pointer unless you know what you are
12+
* doing.
13+
*
14+
* /!\ Modifying the next pointer of the current node will change which node
15+
* comes next in the iteration.
16+
*
17+
* @param {Node} first First node of the list.
18+
* @return {IterableIterator<Node>} Yields nodes of a list in order.
19+
*/
20+
export default function* _iter_fast(first) {
21+
assert(isNonEmpty(first));
22+
let next = first;
23+
24+
do {
25+
yield next;
26+
next = _shift(next);
27+
} while (!isEmpty(next));
28+
}

src/_last.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isNonEmpty from './isNonEmpty.js';
6+
import _isLast from './_isLast.js';
7+
import _shift from './_shift.js';
8+
9+
/**
10+
* Return the last node of a non-empty input list.
11+
*
12+
* @param {Node} x First node of the input list.
13+
* @return {Node} Last node of the input list.
14+
*/
15+
export default function _last(x) {
16+
assert(isNonEmpty(x));
17+
while (!_isLast(x)) {
18+
x = _shift(x);
19+
}
20+
21+
return x;
22+
}

src/_len.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isEmpty from './isEmpty.js';
6+
import isNonEmpty from './isNonEmpty.js';
7+
import _shift from './_shift.js';
8+
9+
/**
10+
* Compute the length of a non-empty list.
11+
*
12+
* @param {Node} x First node of the input list.
13+
* @return {number} The length of the input list.
14+
*/
15+
export default function _len(x) {
16+
assert(isNonEmpty(x));
17+
let n = 1;
18+
let y = _shift(x);
19+
while (!isEmpty(y)) {
20+
++n;
21+
y = _shift(y);
22+
}
23+
24+
return n;
25+
}

src/_pop.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import empty from './empty.js';
6+
import isNonEmpty from './isNonEmpty.js';
7+
import _isLast from './_isLast.js';
8+
import _setNext from './_setNext.js';
9+
import _shift from './_shift.js';
10+
11+
/**
12+
* Removes last {@link Node} from a non-empty list.
13+
*
14+
* @param {Node} x First node of input list (non-empty).
15+
* @return {[Node, Node]} New list (possibly empty) and removed node.
16+
*/
17+
export default function _pop(x) {
18+
assert(isNonEmpty(x));
19+
if (_isLast(x)) return [empty(), x];
20+
let penultimate = x;
21+
let last = _shift(x);
22+
while (!_isLast(last)) {
23+
penultimate = last;
24+
last = _shift(last);
25+
}
26+
27+
_setNext(penultimate, empty());
28+
return [x, last];
29+
}

src/_setNext.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isList from './isList.js';
6+
import isNonEmpty from './isNonEmpty.js';
7+
8+
/**
9+
* Replace successor of the input node.
10+
*
11+
* @param {Node} x Node to replace the sucessor of.
12+
* @param {Node} y New successor (possibly null).
13+
*/
14+
export default function _setNext(x, y) {
15+
assert(isNonEmpty(x));
16+
assert(isList(y));
17+
x._next = y;
18+
}

src/_setValue.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isNonEmpty from './isNonEmpty.js';
6+
7+
/**
8+
* Set value held by input node.
9+
*
10+
* @param {Node} x Input node (cannot be null).
11+
* @param {any} value
12+
*/
13+
export default function _setValue(x, value) {
14+
assert(isNonEmpty(x));
15+
x._value = value;
16+
}

src/_shift.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isNonEmpty from './isNonEmpty.js';
6+
7+
/**
8+
* Removes first {@link Node} from a non-empty list.
9+
*
10+
* @param {Node} x First node (not null).
11+
* @return {Node} New list (possibly empty).
12+
*/
13+
export default function _shift(x) {
14+
assert(isNonEmpty(x));
15+
return x._next;
16+
}

src/_value.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isNonEmpty from './isNonEmpty.js';
6+
7+
/**
8+
* Return the first value held by a non-empty list.
9+
*
10+
* @param {Node} x First node.
11+
* @return {any}
12+
*/
13+
export default function value(x) {
14+
assert(isNonEmpty(x));
15+
return x._value;
16+
}

src/concat.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import assert from 'assert';
2+
3+
// eslint-disable-next-line no-unused-vars
4+
import Node from './Node.js';
5+
import isEmpty from './isEmpty.js';
6+
import isNonEmpty from './isNonEmpty.js';
7+
import _setNext from './_setNext.js';
8+
import _last from './_last.js';
9+
10+
/**
11+
* Concatenate two input lists.
12+
*
13+
* @param {Node} x First node of first input list (can empty).
14+
* @param {Node} y First node of second input list (can empty).
15+
* @return {Node} First node of the output list (or an empty list).
16+
*/
17+
export default function concat(x, y) {
18+
if (isEmpty(x)) return y;
19+
assert(isNonEmpty(x));
20+
if (isEmpty(y)) return x;
21+
assert(isNonEmpty(y));
22+
_setNext(_last(x), y);
23+
return x;
24+
}

src/empty.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Return an empty list.
3+
*
4+
* @return {null} The empty list.
5+
*/
6+
export default function empty() {
7+
return null;
8+
}

src/from.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// eslint-disable-next-line no-unused-vars
2+
import Node from './Node.js';
3+
import _setNext from './_setNext.js';
4+
import empty from './empty.js';
5+
import single from './single.js';
6+
7+
/**
8+
* Creates a list from an input iterable.
9+
*
10+
* @param {IterableIterator} iterable The input iterable.
11+
* @return {Node} First node of the newly created list (or null if empty list).
12+
*/
13+
export default function from(iterable) {
14+
const it = iterable[Symbol.iterator]();
15+
const event = it.next();
16+
17+
if (event.done) return empty();
18+
19+
const first = single(event.value);
20+
let last = first;
21+
22+
for (const value of it) {
23+
const next = single(value);
24+
_setNext(last, next);
25+
last = next;
26+
}
27+
28+
return first;
29+
}

src/index.js

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,29 @@
1-
const answer = 42;
2-
export default answer;
1+
export {default as Node} from './Node.js';
2+
export {default as _isLast} from './_isLast.js';
3+
export {default as _iter} from './_iter.js';
4+
export {default as _iter_fast} from './_iter_fast.js';
5+
export {default as _last} from './_last.js';
6+
export {default as _len} from './_len.js';
7+
export {default as _pop} from './_pop.js';
8+
export {default as _setNext} from './_setNext.js';
9+
export {default as _setValue} from './_setValue.js';
10+
export {default as _shift} from './_shift.js';
11+
export {default as _value} from './_value.js';
12+
export {default as concat} from './concat.js';
13+
export {default as empty} from './empty.js';
14+
export {default as from} from './from.js';
15+
export {default as isEmpty} from './isEmpty.js';
16+
export {default as isLast} from './isLast.js';
17+
export {default as isList} from './isList.js';
18+
export {default as isNonEmpty} from './isNonEmpty.js';
19+
export {default as iter} from './iter.js';
20+
export {default as last} from './last.js';
21+
export {default as len} from './len.js';
22+
export {default as pop} from './pop.js';
23+
export {default as push} from './push.js';
24+
export {default as setValue} from './setValue.js';
25+
export {default as shift} from './shift.js';
26+
export {default as single} from './single.js';
27+
export {default as unshift} from './unshift.js';
28+
export {default as value} from './value.js';
29+
export {default as values} from './values.js';

src/isEmpty.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import empty from './empty.js';
2+
3+
/**
4+
* Return whether the input is a list and is empty.
5+
*
6+
* @param {any} x Input.
7+
* @return {boolean} True iff the input is an empty list.
8+
*/
9+
export default function isEmpty(x) {
10+
return x === empty();
11+
}

0 commit comments

Comments
 (0)