user718954
user718954

Reputation:

Constructing a sequence/linked list through inheritance recursively in JavaScript

Background: I'm reading the online book Eloquent JavaScript and one of the exercises in Chapter 6 mentions that a "Sequence" can behave similarly to a linked list. Here is the link to the exercise, and I've copied a bit of relevant text:

Another solution is to avoid changing state in the object. You can expose a method for getting the current element (without advancing any counter) and another for getting a new sequence that represents the remaining elements after the current one (or a special value if the end of the sequence is reached).

I am trying to build a Sequence by recursively calling the implementing class's (ArraySeq) constructor within the interface class. However when running a test in node, I'm getting TypeError: Cannot read property '0' of undefined at Sequence.ArraySeq.

I've copied and pasted my (incomplete) implementation:

/**
 * Sequence interface
 */
function Sequence(current, rest) {
  this.current = current;
  this.rest = rest;
}

Object.defineProperty(Sequence.prototype, "end", {
  get: function() {
    return this.rest === undefined;
  }
});

Sequence.prototype.next = function() {
  return this.rest;
};

/**
 * Array implementation of sequence
 */
function ArraySeq(array) {
  if (array === []) {
    Sequence.call(undefined, undefined);
  } else {
    Sequence.call(array[0], new ArraySeq(array.slice(1)));
  }
}

ArraySeq.prototype = Object.create(Sequence.prototype);

/**
 * Logs all elements in a Sequence
 */
function logSequence(sequence) {
  while (sequence.rest !== undefined) {
    console.log(sequence.current);
    sequence = sequence.rest;
  }
}

logSequence(new ArraySeq([1, 2]));

Thank you for reading this far, any help or guidance is greatly appreciated!

Upvotes: 0

Views: 101

Answers (2)

Amadan
Amadan

Reputation: 198334

As I noted in comments:

  • array.splice[1] will give you undefined. You want array.slice(1) - the whole array without the first element: slice, not splice. array.splice(1) will delete the second element from the array and return that element - not what you want.

  • You wrote Sequence as a constructor, but you are not calling it as a constructor. Instead of Sequence.call, use new Sequence.

  • Conversely, you are calling new ArraySeq, but ArraySeq does not look like a constructor. Use just ArraySeq, and make it return stuff (return new Sequence...).

  • Use if (!array.length) to test for array being non-empty. array === [], and even array == [], can never return true, because objects (and thus arrays) are compared based on object identity, not equality, and you just created a new one (so there is no chance it is the same object as something that already existed).

  • And of course, ArraySequence is not defined; that was supposed to be ArraySeq, right?

With those changes, your code works. EDIT: However, the exercise wants ArraySeq to be an object, so still a bit more work... First of all, "interface" is not an object. It is just how an object should behave. My go at the exercise would be:

function ArraySeq(array) {
  this.array = array;
  this.index = 0;
}

Object.defineProperty(ArraySeq.prototype, "end", {
  get: function() {
    return this.index >= this.array.length;
  }
});

Object.defineProperty(ArraySeq.prototype, "next", {
  get: function() {
    return this.array[this.index++];
  }
});


/**
 * Logs all elements in a Sequence
 */
function logSequence(sequence) {
  while (!sequence.end) {
    console.log(sequence.next);
  }
}

logSequence(new ArraySeq([1, 2]));

The "interface" here is .end and .next. If you want to go the route of your quote, then it changes slightly. The interface here is .end, .rest and .value:

function ArraySeq(array) {
  this.array = array;
}

Object.defineProperty(ArraySeq.prototype, "end", {
  get: function() {
    return this.array.length == 0;
  }
});

Object.defineProperty(ArraySeq.prototype, "rest", {
  get: function() {
    return new ArraySeq(this.array.slice(1));
  }
});

Object.defineProperty(ArraySeq.prototype, "value", {
  get: function() {
    return this.array[0];
  }
});


/**
 * Logs all elements in a Sequence
 */
function logSequence(sequence) {
  while (!sequence.end) {
    console.log(sequence.value);
    sequence = sequence.rest;
  }
}

logSequence(new ArraySeq([1, 2]));

Upvotes: 1

jinsoo yoon
jinsoo yoon

Reputation: 106

first, splice is method.

array.splice(1) instead of array.splice[1].

and use array.length == 0 in array === [].

if two object is different object, then === operator treated as false even all element is same.

Upvotes: 0

Related Questions