質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
ECMAScript

ECMAScriptとは、JavaScript類の標準を定めるために作られたスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

525閲覧

イテレータを進めずに終了しているかどうかを取得する方法はありますか?

Lhankor_Mhy

総合スコア36115

ECMAScript

ECMAScriptとは、JavaScript類の標準を定めるために作られたスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2019/03/28 11:55

編集2019/03/29 08:27

js

1const iter = Array.from( new Array(10), (_,i)=>i )[Symbol.iterator](); // イテレータ(仮) 2let [x,y,z] = iter; 3if ( ! iter.next().done ){ 4 [x,y,z] = iter; 5} 6console.log(x,y,z); 7/* 84,5,6 9*/

頭書コードですが、イテレータが終了していなければ再度代入して値を取得する、というコードなのですが、終了しているかどうかを判定するために.next().doneとしているため、値がひとつ進んでしまいます。
これを値を進めずに、イテレータが終了しているかどうか判別する方法はありますか?
(ECMAScriptの仕様候補にあれば、ブラウザ未実装の機能でも構いません。)

もちろん、

js

1let x = iter.next(); 2let y = iter.next(); 3let z = iter.next(); 4if ( ! z.done ){ 5 x = iter.next(); 6 y = iter.next(); 7 z = iter.next(); 8} 9x = x.value; 10y = y.value; 11z = z.value;

とすればいいのはわかりますが、分割代入もしたいのです。

配列にするのが手っ取り早いというのは理解していますので、純粋に「できるのかな?」という疑問であるとお読みいただければと。
よろしくお願いいたします。

解決されたコード

think49 さんのご回答で解決しました。
自前ではないイテレータで使いたかったので、ラッパ関数を作ってみました。

js

1const originalIter = Array.from( new Array(10), (_,i)=>i )[Symbol.iterator](); 2 3const iterWrapper = function(iter){ 4 let _done = false; 5 return { 6 next(){ 7 const _next = iter.next(); 8 _done = _next.done; 9 return _next; 10 }, 11 //_done:false, 12 done(){ 13 return _done; 14 }, 15 [Symbol.iterator](){ 16 return this; 17 } 18 } 19} 20 21const iter = iterWrapper(originalIter); 22 23let [x,y,z] = iter; 24if (!iter.done()){ 25 [x,y,z] = iter; 26} 27 28console.log(x,y,z); 29/* 303,4,5 31*/

また、補足欄でのご指摘も非情に示唆に富むものでした。
イテレータが iterable とは限らない、というのは目から鱗です。
自前でイテレータを実装する時は、

js

1 [Symbol.iterator](){ 2 return this; 3 }

が必須じゃないか、と思いました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2019/03/28 14:24

イテレータを分割代入しようとしているコードに違和感があります。 分割代入が機能するのは、iter が偶然にも iter[Symbol.iterator]を持っていた、つまりは iterable だったからです。 この場合、[x,y,z] = iter の分割代入と iter.next() には因果関係がないと思うのですが…。
Lhankor_Mhy

2019/03/29 03:49

なるほど、イテレータは必ずしも iterable ではない、ということですね。 そして、分割代入はイテレータであっても @@iterator を呼ぶ、と。なるほど、不勉強でした。 iterable なオブジェクトは @@iterator がイテレータを返すから iterable であるわけで、であるならば、イテレータならそのまま使われるだろう、と勘違いしていました。 試してみると、ジェネレータで生成したイテレータについては、JavaScriptがよきに計らってくださるようで、@@iterator も一緒に生成されていたので、なおのこと、この勘違いに気づきませんでした。
guest

回答1

0

ベストアンサー

getter/setter (ver.1)

getter/setter を駆使して、iterable に done 値を引き継げば、期待通りに動作すると思います。

JavaScript

1'use strict'; 2const iterable = { 3 i: 0, 4 done: false, 5 [Symbol.iterator]: function () { 6 const iterable = this; 7 8 return { 9 get i () { return iterable.i; }, 10 set i (i) { return iterable.i = i }, 11 get done () { return iterable.done; }, 12 set done (done) { return iterable.done = done }, 13 length: 10, 14 next: function next () { 15 return { 16 value: this.i++, 17 done: this.done = this.i > this.length 18 }; 19 } 20 }; 21 } 22}; 23 24let x, y, z; 25 26if (!iterable.done) { 27 [x, y, z] = iterable; 28} 29 30console.log(x, y, z); // 0 1 2 31console.log(iterable); // i: 3, done: false, Symbol(Symbol.iterator): ƒ}

ただし、done 値を iterable から得るという事は、全てにおいて done 値を共有するという事です。

JavaScript

1for (let v of iterable) console.log(v); // 反復する 2console.log(iterable.done); // true 3for (let v of iterable) console.log(v); // done 値を教諭している為、反復しない 4 5const array = [1,2,3]; 6for (let v of array) console.log(v); // 反復する 7for (let v of array) console.log(v); // 反復する

このように、本来、何度でも反復可能なはずの機能が1回限りの反復処理になります。
「期待する動作」のロジック上、この挙動は不可避なので、その要件が本当に必要なものかどうか、を良く吟味する必要があると考えます。

(追記) この問題はver.2で解消されました。

getter/setter (ver.2)

iterator から iterator.done を参照(getter)せず、書き込み(setter)のみを行う事で、done 先の共有問題は解消されます。

JavaScript

1const iterable = { 2 done: false, 3 [Symbol.iterator]: function () { 4 const iterable = this; 5 let _done = false; 6 7 return { 8 i: 0, 9 get done () { return _done; }, 10 set done (input) { return _done = iterable.done = input }, 11 length: 10, 12 next: function next () { 13 return { 14 value: this.i++, 15 done: this.done = this.i > this.length 16 }; 17 } 18 }; 19 } 20}; 21 22for (let v of iterable) console.log(v); // 反復する 23console.log(iterable.done); // true 24for (let v of iterable) console.log(v); // 反復する

更新履歴

  • 2019/03/28 22:48 要件解釈に誤りがあった為、getter/setter型にコードを修正
  • 2019/03/28 23:08 「getter/setter (ver.2)」を追記

Re: Lhankor_Mhy さん

投稿2019/03/28 13:17

編集2019/03/28 14:09
think49

総合スコア18164

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lhankor_Mhy

2019/03/29 03:34

なるほど、これは興味深いです。 ですが、ちょっとひっかかるところがあったので、いろいろ試してみます。
Lhankor_Mhy

2019/03/29 08:11

ver.1 が想定どおりの動作でした。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問