// Doubly linked list. (3.01)
function _ListNode(value) {
this._next = this;
this._prev = this;
this.value = value;
}
Object.defineProperty(_ListNode.prototype, 'next', {
get: function () {
return this._next;
}
});
Object.defineProperty(_ListNode.prototype, 'prev', {
get: function () {
return this._prev;
}
});
_ListNode.prototype.insert = function (value) {
let node = new _ListNode(value);
node._next = this;
node._prev = this._prev;
this._prev._next = node;
this._prev = node;
return this;
};
_ListNode.prototype.remove = function (last = this._next) {
let next = last;
let prev = this._prev;
next._prev = prev;
prev._next = next;
return next;
};
_ListNode.prototype.splice = function (first, last = first._next) {
if (first === last || last === this)
return;
last._prev._next = this;
first._prev._next = last;
this._prev._next = first;
let temp = this._prev;
this._prev = last._prev;
last._prev = first._prev;
first._prev = temp;
return this;
};
_ListNode.prototype.step = function (n = 1) {
let result = this;
for (let i = 0; i < n; i++) {
result = result._next;
}
for (let i = 0; i > n; i--) {
result = result._prev;
}
return result;
};
_ListNode.prototype.distance = function (last) {
let result = 0;
for (let first = this; first !== last; first = first._next) {
result++;
}
return result;
};
//
// List.
//
function List(iterable = []) {
this.head = new _ListNode();
for (const x of iterable) {
this.pushBack(x);
}
}
Object.defineProperty(List.prototype, 'begin', {
get: function () {
return this.head.next;
}
});
Object.defineProperty(List.prototype, 'end', {
get: function () {
return this.head;
}
});
Object.defineProperty(List.prototype, 'empty', {
get: function () {
return this.begin === this.end;
}
});
Object.defineProperty(List.prototype, 'front', {
get: function () {
if (this.empty)
throw new Error("Data access in empty list");
return this.begin.value;
}
});
Object.defineProperty(List.prototype, 'back', {
get: function () {
if (this.empty)
throw new Error("Data access in empty list");
return this.end.prev.value;
}
});
List.prototype.pushFront = function (value) {
this.begin.insert(value);
return this;
};
List.prototype.pushBack = function (value) {
this.end.insert(value);
return this;
};
List.prototype.popFront = function () {
if (this.empty)
throw new Error("Pop in empty list");
this.begin.remove();
return this;
};
List.prototype.popBack = function () {
if (this.empty)
throw new Error("Pop in empty list");
this.end.prev.remove();
return this;
};
List.prototype.clear = function () {
this.begin.remove(this.end);
return this;
};
List.prototype[Symbol.iterator] = function* () {
const last = this.end;
for (let first = this.begin; first !== last; first = first.next) {
yield first.value;
}
};
List.prototype.forEach = function (callbackFn, ...args) {
for (const x of this) {
callbackFn(x, ...args);
}
};
List.prototype.map = function (callbackFn, ...args) {
let result = new List();
this.forEach(x => result.pushBack(callbackFn(x, ...args)));
return result;
};
List.prototype.toString = function () {
return `List {${Array.from(this).join(", ")}}`;
};
//
// Main.
//
let list1 = new List();
for (let i = 0; i < 5; i++) {
list1.pushBack(i);
console.log("1." + list1);
}
console.log("1.size:", list1.begin.distance(list1.end));
console.log("1.empty:", list1.empty);
console.log();
while (!list1.empty) {
list1.popFront()
console.log("1." + list1);
}
console.log("1.size:", list1.begin.distance(list1.end));
console.log("1.empty:", list1.empty);
console.log();
for (let i = 0; i < 5; i++) {
list1.pushFront(i);
}
console.log("1." + list1);
console.log("1.size:", list1.begin.distance(list1.end));
console.log();
let list2 = new List([10, 11, 12, 13]);
console.log("2." + list2);
console.log("2.size:", list2.begin.distance(list2.end));
console.log();
console.log("1.begin.next.splice(2.begin, 2.end)");
list1.begin.next.splice(list2.begin, list2.end);
console.log("1." + list1);
console.log("1.size:", list1.begin.distance(list1.end));
console.log();
console.log("2." + list2);
console.log("2.size:", list2.begin.distance(list2.end));
console.log();
console.log("1.begin.next.remove(1.end.step(-4))");
list1.begin.next.remove(list1.end.step(-4));
console.log("1." + list1);
console.log("1.size:", list1.begin.distance(list1.end));
console.log("1.front:", list1.front);
console.log("1.back:", list1.back);
console.log();
try {
list2.front();
} catch (error) {
console.log("2.front:", error.message);
}
try {
list2.back();
} catch (error) {
console.log("2.back:", error.message);
}
try {
list2.popFront();
} catch (error) {
console.log("2.popFront:", error.message);
}
try {
list2.popBack();
} catch (error) {
console.log("2.popBack:", error.message);
}