ジェネレータの return は [...gen()] したときに含まれない
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ ジェネレータ中の return は特別
◆ そこで終わるだけじゃなくて done を返す場合に返す値の使われ方も変わる
◆ [...gen()] の配列化などでは done のときの値は含まれない
◆ yield* でも done のときの値は含まれない
◆ そこで終わるだけじゃなくて done を返す場合に返す値の使われ方も変わる
◆ [...gen()] の配列化などでは done のときの値は含まれない
◆ yield* でも done のときの値は含まれない
久々に generator を使ったら思ってたのと違う動きで戸惑いました
の結果は
です
ここまでは想定どおりです
では
これを実行した場合は?
done のところが 3 なので 1, 2, 3 の配列に思いました
しかし
配列に含まれるのは yield で返した値のみで return で返した値 (done のとき) は配列に含まれないようです
return は通常の値ではなく終了理由とかそういう特殊な値を返す用途に使うことが想定されてるのでしょうか
とりあえず 条件を満たしたら最後の値を返しておしまいってときに return で値を返すのはダメみたいです
これでは最後に 100 は入らないので 100 も含めたいならこうします
単純に yield のあとに return を書くだけです
上のトラブルに出会ったのは単純なジェネレータではなく再帰的なジェネレータでちょっと複雑なものでした
特に意味はない処理ですが 構造はこういう感じです
[...t()] で空になる理由がわからなくて とりあえず .next() の結果を一つ確認と console.log を置いてみたら console.log が 2 回実行されていました
その時の予想では yield* で任せてしまってるところで return してるから最初の 1 つの要素だけ帰ってくるというものです
return でおしまいなので console.log の部分は通らないと思ってました
そういうこともあってジェネレータがよくわからなくなってきていたのですが 上の通り return したのは [...] では含まれないとわかれば納得できる動きでした
[...] の配列化と同じように yield* でも return のときの値は無視されます
なので yield* sub(~) のところは返す結果なしで終わります
1 つ目の next の結果が見つからないので 結果が見つかるまで処理が続行されます
そして次の行が普通に実行されて console.log が呼び出されます
全再帰処理で同じように yield* で返す値がないので結局最後まで実行されておしまいとなっていました
このせいで ジェネレータって done になったらあとは結果返さないだけで最後までとりあえず実行されるの?なんて変な方向に考えていました
return 時の扱いが特別なのはジェネレータ使う上で思った以上に重要ですね
function* t() {
yield 1
yield 2
return 3
}
const x = t()
console.log(x.next())
console.log(x.next())
console.log(x.next())
console.log(x.next())
の結果は
{value: 1, done: false}
{value: 2, done: false}
{value: 3, done: true}
{value: undefined, done: true}
です
ここまでは想定どおりです
では
console.log([...t()])
これを実行した場合は?
done のところが 3 なので 1, 2, 3 の配列に思いました
しかし
[1, 2]
配列に含まれるのは yield で返した値のみで return で返した値 (done のとき) は配列に含まれないようです
return は通常の値ではなく終了理由とかそういう特殊な値を返す用途に使うことが想定されてるのでしょうか
とりあえず 条件を満たしたら最後の値を返しておしまいってときに return で値を返すのはダメみたいです
function* t() {
while (true) {
if (~~(Math.random() * 2)) {
return 100
} else {
yield 1
}
}
}
console.log([...t()])
// 100 は入らない
これでは最後に 100 は入らないので 100 も含めたいならこうします
function* t() {
while (true) {
if (~~(Math.random() * 2)) {
yield 100
return
} else {
yield 1
}
}
}
console.log([...t()])
単純に yield のあとに return を書くだけです
おまけ
ついでにもうひとつ yield* ではまりました上のトラブルに出会ったのは単純なジェネレータではなく再帰的なジェネレータでちょっと複雑なものでした
function* t(values) {
function* sub(result, arr) {
const v = arr.shift()
if(v % 3 === 0){
return result
}else{
yield* sub([...result, v], arr)
console.log(".")
}
}
yield* sub([], values)
}
console.log(t([1, 2, 3, 4, 5, 6]).next())
// {value: undefined, done: true}
特に意味はない処理ですが 構造はこういう感じです
[...t()] で空になる理由がわからなくて とりあえず .next() の結果を一つ確認と console.log を置いてみたら console.log が 2 回実行されていました
その時の予想では yield* で任せてしまってるところで return してるから最初の 1 つの要素だけ帰ってくるというものです
return でおしまいなので console.log の部分は通らないと思ってました
そういうこともあってジェネレータがよくわからなくなってきていたのですが 上の通り return したのは [...] では含まれないとわかれば納得できる動きでした
[...] の配列化と同じように yield* でも return のときの値は無視されます
なので yield* sub(~) のところは返す結果なしで終わります
1 つ目の next の結果が見つからないので 結果が見つかるまで処理が続行されます
そして次の行が普通に実行されて console.log が呼び出されます
全再帰処理で同じように yield* で返す値がないので結局最後まで実行されておしまいとなっていました
このせいで ジェネレータって done になったらあとは結果返さないだけで最後までとりあえず実行されるの?なんて変な方向に考えていました
return 時の扱いが特別なのはジェネレータ使う上で思った以上に重要ですね