21

整数のリストがあるとしましょう:

var fibonacci = [1,1,2,3,5,8,13,21];

次の要素と前の要素を次の方法で取得できるようにしたい(たとえば、配列を変更せずに要素ポインターを移動するだけです)(たとえば、プロトタイプなしで配列インターフェイスを再定義する場合がありますが、そうではありません)。

fibonacci.prev(); // returns false
fibonacci.next(); // returns 1
fibonacci.next(); // returns 1
fibonacci.next(); // returns 2
fibonacci.next(); // returns 3
fibonacci.next(); // returns 5
fibonacci.next(); // returns 8

fibonacci.prev(); // returns 5

fibonacci.next(); // returns 8
fibonacci.next(); // returns 13
fibonacci.next(); // returns false
4

5 に答える 5

40

リストをとして保持する場合は、リストArrayを変更し[[prototype]]て、反復可能なコレクションのように見せることができます。

Array.prototype.next = function() {
    return this[++this.current];
};
Array.prototype.prev = function() {
    return this[--this.current];
};
Array.prototype.current = 0;

これで、すべてArrayにメソッドprevと、、nextおよびcurrent「現在の」要素を指すプロパティがあります。警告:currentプロパティは変更される可能性があるため、予測できない結果になります。

スクリプト後:インデックスが範囲外のときに作成prevしてnext返すことはお勧めしません。false本当に必要な場合は、メソッドを次のように変更できます。

Array.prototype.next = function() {
    if (!((this.current + 1) in this)) return false;
    return this[++this.current];
};

2016年半ばに更新

まだ意見や投票を受け取っているようですので、この回答を更新しています。与えられた答えは概念実証であり、一般にネイティブクラスのプロトタイプを拡張することは悪い習慣であり、本番プロジェクトでは避けるべきであることを明確にすべきでした。

特に、サイクルが混乱するため、それほど多くはありませfor...inん。これは、配列では常に回避する必要があり、要素を反復処理するのは間違いなく悪い習慣です。また、IE9以降、代わりにこれを確実に実行できるためです。

Object.defineProperty(Array.prototype, "next", {
    value: function() { return this[++this.current]; },
    enumerable: false
});

主な問題は、ネイティブクラスの拡張が将来にわたって利用できないことです。つまり、ECMAがnext、実装と互換性がない可能性のある配列のメソッドを導入する可能性があります。それは非常に一般的なJSフレームワークでもすでに起こりました-最後のケースはMooToolsのcontains配列拡張であり、ECMAは名前をincludes(悪い動き、IMO、のようなオブジェクトcontainsにすでにあるので)に変更しました。DOMTokenListElement.classList

そうは言っても、ネイティブプロトタイプを拡張してはいけないということではありませんが、自分が何をしているのかを知っておく必要があります。私があなたに与えることができる最初のアドバイスは、例えばmyCompanyNext単にの代わりに、将来の標準的な拡張と衝突しない名前を選ぶことですnext。これはあなたにいくらかのコードエレガンスを要しますが、あなたはぐっすり眠ります。

さらに良いことに、この場合、Arrayクラスを効果的に拡張できます。

function MyTraversableArray() {
    if (typeof arguments[0] === "number")
        this.length = arguments[0];
    else this.push.apply(this, arguments);

    this.current = 0;
}
MyTraversableArray.prototype = [];
MyTraversableArray.prototype.constructor = MyTraversableArray;
MyTraversableArray.prototype.next = function() {
    return this[++this.current];
};
MyTraversableArray.prototype.prev = function() {
    return this[--this.current];
};

さらに、ES6では、ネイティブクラスを拡張する方が簡単です。

class MyTraversableArray extends Array {
    next() {
        return this[++this.current];
    }
}

残念ながら、トランスパイラーはネイティブクラスの拡張機能に苦労しており、Babelはそのサポートを削除しました。しかし、それは、私たちの場合には影響を及ぼさないいくつかの動作を正確に複製できないためです。したがって、上記の古いES3コードを使用することができます。

于 2012-09-12T14:39:26.797 に答える
23

Array.prototype本当に悪いJavaScriptがたくさんあるので、私は一般的に物を追加しないことをお勧めします。たとえば、設定Array.protoype.next = function () {}して誰かが次のコードを持っている場合、問題があります。

var total = 0, i, myArr = [0,1,2];
for(i in myArr) {
    total += myArr[i];
}
total; //is probably "3next"

for-inループのこの悪い使用法は、世の中には気がかりなほど一般的です。だから、あなたはArrayのプロトタイプに追加することによって問題を求めています。ただし、目的の処理を実行するラッパーを作成するのは非常に簡単です。

var iterifyArr = function (arr) {
    var cur = 0;
    arr.next = (function () { return (++cur >= this.length) ? false : this[cur]; });
    arr.prev = (function () { return (--cur < 0) ? false : this[cur]; });
    return arr;
};

var fibonacci = [1, 1, 2, 3, 5, 8, 13];
iterifyArr(fibonacci);

fibonacci.prev(); // returns false
fibonacci.next(); // returns 1
fibonacci.next(); // returns 1
fibonacci.next(); // returns 2
fibonacci.next(); // returns 3
fibonacci.next(); // returns 5
fibonacci.next(); // returns 8
fibonacci.prev(); // returns 5
fibonacci.next(); // returns 8
fibonacci.next(); // returns 13
fibonacci.next(); // returns false

いくつかのメモ:

まず第一に、あなたはおそらくあなたが終わりを過ぎた場合undefinedの代わりにそれを返してもらいたいでしょう。false次に、このメソッドはcurクロージャーを使用して非表示になるため、配列でこのメソッドにアクセスできません。したがってcur()、現在の値を取得するメソッドが必要になる場合があります。

//Inside the iterifyArr function:
    //...
    arr.cur = (function () { return this[cur]; });
    //...

最後に、「ポインタ」が最後までどれだけ維持されているかについての要件は明確ではありません。たとえば、次のコードを考えてみましょう(fibonacci上記のように設定されていると仮定します)。

fibonacci.prev(); //false
fibonacci.prev(); //false
fibonacci.next(); //Should this be false or 1?

私のコードではそうなるでしょうがfalse、あなたはそれを望むかもしれません1。その場合、私のコードにいくつかの簡単な変更を加える必要があります。

ああ、関数が返すarrので、次のように、定義したのと同じ行で配列を「反復化」できます。

var fibonacci = iterifyArr([1, 1, 2, 3, 5, 8, 13]);

それはあなたにとって物事を少しきれいにするかもしれません。配列を再度呼び出すことでイテレータをリセットすることもできiterifyArrます。または、非常に簡単にリセットするメソッドを作成することもできます(cur0に設定するだけです)。

于 2012-09-12T15:16:58.807 に答える
6

ES2015の時点では、配列は反復可能であるため、これの次の側面は配列に組み込まれています。つまり、メソッドを持つ反復子を取得できます(ただし、「前の」部分を読み続けてください)。next

const a = [1, 2, 3, 4, 5];
const iter = a[Symbol.iterator]();
let result;
while (!(result = iter.next()).done) {
  console.log(result.value);
}

ただし、イテレータは前進するだけで、双方向ではありません。そしてもちろん、通常はイテレータを明示的に使用せず、通常、次のような反復構造の一部として使用しますfor-of

const a = [1, 2, 3, 4, 5];
for (const value of a) {
  console.log(value);
}

双方向のイテレータを簡単に作成できます。

  1. 配列を受け入れてイテレータを返すスタンドアロン関数を作成するか、または

  2. サブクラスArray内のイテレータをサブクラス化してオーバーライドするか、または

  3. デフォルトのArrayイテレータを独自のイテレータに置き換える(先に進むときは、デフォルトのイテレータとまったく同じように機能することを確認してください)。

サブクラスの例を次に示します。

class MyArray extends Array {
  // Define the iterator function for this class
  [Symbol.iterator]() {
    // `index` points at the next value we'll return
    let index = 0;
    // Return the iterator
    return {
      // `next` returns the next
      next: () => {
        const done = index >= this.length;
        const value = done ? undefined : this[index++];
        return { value, done };
      },
      // `prev` returns the previous
      prev: () => {
        const done = index == 0;
        const value = done ? undefined : this[--index];
        return { value, done };
      }
    };
  }
}

// Demonstrate usage:
const a = new MyArray("a", "b");
const i = a[Symbol.iterator]();
console.log("next", JSON.stringify(i.next()));
console.log("next", JSON.stringify(i.next()));
console.log("next", JSON.stringify(i.next()));
console.log("prev", JSON.stringify(i.prev()));
console.log("prev", JSON.stringify(i.prev()));
console.log("prev", JSON.stringify(i.prev()));
console.log("next", JSON.stringify(i.next()));
.as-console-wrapper {
  max-height: 100% !important;
}

于 2018-01-22T09:23:40.070 に答える
3

ES6は、以下のように非常に簡単に配列を出力できるジェネレーター関数を提供します。

function* data() {
  yield* [1, 1, 2, 3, 5, 8, 13, 21];
}

var fibonnacci = data();

fibonnacci.next()
> {value: 1, done: false}

fibonnacci.next()
> {value: 1, done: false}

fibonnacci.next()
> {value: 2, done: false}

fibonnacci.next()
> {value: 3, done: false}

fibonnacci.next()
> {value: 5, done: false}

fibonnacci.next()
> {value: 8, done: false}

fibonnacci.next()
> {value: 13, done: false}

fibonnacci.next()
> {value: 21, done: false}

fibonnacci.next()
> {value: undefined, done: true}

ただし、サンプルプログラムはMDNドキュメントに存在し、フィボナッチ数列を目的の要素まで印刷するのに役立ちます。

function* fibonacci() {
  var fn1 = 0;
  var fn2 = 1;
  while (true) {  
    var current = fn1;
    fn1 = fn2;
    fn2 = current + fn1;
    var reset = yield current;
    if (reset) {
        fn1 = 0;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value);     // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
于 2018-02-04T21:28:38.310 に答える
1

ネイティブオブジェクトのプロトタイプを変更するよりも安全なのは、必要な追加の配列関数用のファクトリ関数を作成することです。

const moreArrayFunctions = arr => ({
    current: 0,
    arr,
    next(){

        if( this.current >= ( this.arr.length - 1 ) ){
             this.current = this.arr.length - 1;
        }else{
            this.current++;
        }

        return this.arr[this.current];
    },
    prev(){

        if( this.current <= 0 ){
            this.current = 0;
        }else{
            this.current--;
        }

        return this.arr[this.current];
    }
});

const fibonacci = moreArrayFunctions([1,1,2,3,5,8,13,21]);

fibonacci.next();
fibonacci.prev();
fibonacci.current
于 2019-03-20T06:16:00.347 に答える