2016-07-17 12 views
1

Eloquent JavaScriptを使用しています。問題#6.3(第2版)に関する質問があります。ここでEloquent JavaScript、Sequence Interface

は問題です:

値のコレクションの反復処理を抽象化するインターフェースを設計します。このインターフェイスを提供するオブジェクトは というシーケンスを表し、インターフェイスは何らかの形で などのオブジェクトを使用してシーケンス上で繰り返し処理する必要があります。 という値を見て、それを検索する方法がありますシーケンスの末尾に が到達したときに終了します。

あなたのインターフェイスを指定している

、シーケンス・オブジェクトを受け取り、シーケンスが5つ未満の の要素を持っている場合、その 最初の5つの要素、または少ない上にconsole.logを呼び出す関数 logFiveを書いてみます。

次に、配列をラップするオブジェクト型ArraySeqを実装し、設計したインターフェイスを使用して配列に対して の繰り返し処理を行うことができます。 (そのコンストラクタの引数から引き継いで)整数の範囲を反復する別のオブジェクト型のRangeSeqを実装してください。

私は、このソリューションを書いた:

function ArraySeq(collection) { 
    this.values = collection; 
} 

ArraySeq.prototype.iterate = function(start, end, action) { 
    var n = Math.min(this.values.length, end); 
    for (var i = start; i < n; i++) { 
    action(this.values[i]); 
    } 
}; 

function RangeSeq(from, to) { 
    var array = []; 
    for (var i = from; i <= to; i++) 
    array.push(i); 
    ArraySeq.call(this, array); 
} 

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

function logFive(sequenceObject) { 
sequenceObject.iterate(0, 5, console.log); 
} 

//This code to test how the solution works was provided by the author 
logFive(new ArraySeq([1, 2])); 
// → 1 
// → 2 
logFive(new RangeSeq(100, 1000)); 
// → 100 
// → 101 
// → 102 
// → 103 
// → 104 

作者によるソリューションはここに、異なっているが、それらの両方は、以下のとおりです。

// I am going to use a system where a sequence object has two methods: 
// 
// * next(), which returns a boolean indicating whether there are more 
// elements in the sequence, and moves it forward to the next 
// element when there are. 
// 
// * current(), which returns the current element, and should only be 
// called after next() has returned true at least once. 

function logFive(sequence) { 
    for (var i = 0; i < 5; i++) { 
    if (!sequence.next()) 
     break; 
    console.log(sequence.current()); 
    } 
} 

function ArraySeq(array) { 
    this.pos = -1; 
    this.array = array; 
} 
ArraySeq.prototype.next = function() { 
    if (this.pos >= this.array.length-1) 
    return false; 
    this.pos++; 
    return true; 
}; 
ArraySeq.prototype.current = function() { 
    return this.array[this.pos]; 
}; 

function RangeSeq(from, to) { 
    this.pos = from - 1; 
    this.to = to; 
} 
RangeSeq.prototype.next = function() { 
    if (this.pos >= this.to) 
    return false; 
    this.pos++; 
    return true; 
}; 
RangeSeq.prototype.current = function() { 
    return this.pos; 
}; 

logFive(new ArraySeq([1, 2])); 
// → 1 
// → 2 
logFive(new RangeSeq(100, 1000)); 
// → 100 
// → 101 
// → 102 
// → 103 
// → 104 

// This alternative approach represents the empty sequence as null, 
// and gives non-empty sequences two methods: 
// 
// * head() returns the element at the start of the sequence. 
// 
// * rest() returns the rest of the sequence, or null if there are no 
// elemements left. 
// 
// Because a JavaScript constructor can not return null, we add a make 
// function to constructors of this type of sequence, which constructs 
// a sequence, or returns null if the resulting sequence would be 
// empty. 

function logFive2(sequence) { 
    for (var i = 0; i < 5 && sequence != null; i++) { 
    console.log(sequence.head()); 
    sequence = sequence.rest(); 
    } 
} 

function ArraySeq2(array, offset) { 
    this.array = array; 
    this.offset = offset; 
} 
ArraySeq2.prototype.rest = function() { 
    return ArraySeq2.make(this.array, this.offset + 1); 
}; 
ArraySeq2.prototype.head = function() { 
    return this.array[this.offset]; 
}; 
ArraySeq2.make = function(array, offset) { 
    if (offset == null) offset = 0; 
    if (offset >= array.length) 
    return null; 
    else 
    return new ArraySeq2(array, offset); 
}; 

function RangeSeq2(from, to) { 
    this.from = from; 
    this.to = to; 
} 
RangeSeq2.prototype.rest = function() { 
    return RangeSeq2.make(this.from + 1, this.to); 
}; 
RangeSeq2.prototype.head = function() { 
    return this.from; 
}; 
RangeSeq2.make = function(from, to) { 
    if (from > to) 
    return null; 
    else 
    return new RangeSeq2(from, to); 
}; 

logFive2(ArraySeq2.make([1, 2])); 
// → 1 
// → 2 
logFive2(RangeSeq2.make(100, 1000)); 
// → 100 
// → 101 
// → 102 
// → 103 
// → 104 

私の質問:

  1. マイソリューションは、著者のソリューションとまったく同じ結果をもたらします。私はさまざまなテストケースで3つすべてをテストしました。私は、私の解決策を書くときにこの章の内容を利用しました(この章ではJavaScript、継承などのOOPテクニックについて説明しています)。 このことを考えると、私の解決策は間違っているかどうか?

  2. なぜ彼のソリューションは書かれている方法で書かれていますか?私のアプローチに間違いがありますか?十分に抽象的ではない?私の方法が議論を受け入れるのは悪いことですか?彼の方法は議論を必要としない。 彼のソリューションに比べて私のソリューションに欠点はありますか?(私の解決策は、おそらく問題の私の理解のレベルを反映しているので、私は告白しなければならない、私はやっと、問題を理解する。)

ありがとうございます!

答えて

2

イテレータプロトコルの全体が遅れを繰り返すため、解答が正しくないため、RangeSeq(1, 10000000)は5つだけ必要ならすべての要素でメモリをいっぱいにしません。

著者のコードは大丈夫ですが、不必要に冗長です。イテレータプロトコルは、通常、このように実装されている:

  • のIterable
  • Iterable.iterator()イテレータペアを返すメソッド.nextを提供
  • イテレータオブジェクトを返すiterator()メソッドを有するオブジェクトであります(done, value)。代わりに、.nextは、疲れたイテレータで呼び出されたときに例外をスローすることができます。

例:

function RangeSeq(from, to) { 
 
    this.iterator = function() { 
 
     var i = from; 
 
     return { 
 
      next: function() { return [i >= to, i++] } 
 
     } 
 
    } 
 
} 
 

 
function ArraySeq(ary) { 
 
    this.iterator = function() { 
 
     var i = 0; 
 
     return { 
 
      next: function() { return [ i >= ary.length, ary[i++]] } 
 
     } 
 
    } 
 
} 
 

 
function logN(seq, n) { 
 
    var it = seq.iterator(); 
 

 
    while (n--) { 
 
     let [done, value] = it.next(); 
 
     if (done) break; 
 
     console.log(value) 
 
    } 
 
} 
 

 
logN(new RangeSeq(100, 100000000), 5); 
 

 
console.log('--------------'); 
 

 
logN(new ArraySeq([11,22,33,44,55,66,77,88,99]), 5);

言った、現代のJSエンジンは、内蔵のもののこの種を持っているので、レンジの例では、

としてずっと簡単に書くことができ

function RangeSeq(from, to) { 
 
    this[Symbol.iterator] = function*() { 
 
     for (let i = from; i < to; i++) 
 
      yield i; 
 
     } 
 
} 
 

 
n = 0; 
 
rs = new RangeSeq(100, 10000000); 
 

 
for (let x of rs) { 
 
    if (n++ >= 5) break; 
 
    console.log(x) 
 

 
}

+0

ありがとうございました!分かりました。 –

+0

パターン「return {next:function(){...}}」は、私にとって初めてのパターンです。 "get:function(){...}"と "set:function(){...}"というやや似たようなものがありました。それはたぶん彼のソリューションが冗長である理由でしょう - 彼は読者に紹介したものだけに頼らざるを得ません。 –