20250602 時代

項目 内容 得点 換算点
睡眠時間 6時間40分 87 11.3/13.0
起床 7:34 100 8.0/8.0
散歩 実施・ゴミ拾いあり 100 5.0/5.0
朝食の栄養カバレッジ 3色カバー 100 5.0/5.0
体操 実施 100 5.0/5.0
労働 passion: 80点, discipline: 70点 75 18.0/24.0
ジム 休養日 100 12.0/12.0
勉強会 参加 100 12.0/12.0
個人開発 実施 100 7.0/7.0
あすけん - 68 6.1/9.0
総合 1日の総合評価 - 89

TSKaigiのパワーが抜けてきている。しかし避けがたいことだ。繰り返すが30代はpassionではない。モチベや気合で何かをするのではなく、当たり前にするのだ。睡眠も浅くて中途覚醒が多いので数字ほど良くない。

このツイートを見てだいぶ悲しい気持ちになっている。そうだよなあ。なんか間違っている方向にみんなで進んでいる気がするんだけど、誰も止められないよなあ。ってね。僕も加担してるし。

時代として総括されてみたいという興味がある。僕らは歴史の授業で100年単位、下手すれば1000年単位で各時代を「こういう時代だった」と総括して学ぶ。各時代を生きた人々にとってみれば冗談じゃないという話だろうが、逆に今僕たちが生きているこの時代もいつかはそうなるのだろう。そういう視点を持ってみると、この毎日も違った見え方がするのかも、という興味がある。まあ書いてて思ったけどこれは使い古された「大きな物語」論ですよね。

会社の同期と、2人で北関東の田舎でボロ一軒家でも買って(築50年3桁万円とかいうすごいやつがそこそこある)犬と車買ってDIYして駄菓子屋でも営みながら暮らすか…なんてバカなことを語り合っていた。一度きりの人生だよ。

20250601 Imperative

今日ものんびり起きてDota2、コードリーディング、天気が回復したので布団カバーの洗濯などをしていた。論理午前中にジムで軽く有酸素し、また近所の新デパートに行ってみたが今日も激混みだったので食事は諦め、6月分の二郎枠を消費してきた。ミニ・ニンニク少なめ・アブラ少なめが丁度いい。

帰宅して、極上の昼寝。普段はロフトベッドで寝ているがたまには布団を床におろしてちゃぶ台にお茶を置いて1時間くらいゴロゴロしていた。これが贅沢というものだ。

今日もGeminiとプログラミングについて対話していた。ある処理のあとに別の処理を行うという命令を、JavaScriptでは

f1();
f2();

と書けるが、これ全然自明じゃないよね、みたいな話をしていた。SQLとか、Haskellの遅延評価とかね。JavaScriptの言語仕様の範囲内でもこれの次にこれを行えという指示は上記以外に

f2(f1());

とか

f1().f2();

とか書き方がある。これがf1, f2が非同期処理になったり失敗しうるようになったりするとそれぞれ進化していくが、やはり基本は一番上の命令形になるだろう。

ところでGeminiとずっと話していると思うのは、会話が単線的なのはポテンシャルを浪費しているのではないかということだ。一般的なAIチャットのUIは人間同士で会話するLINEのように自分の発言と相手の発言が交互に時系列的に並ぶだけのシンプルなものだ。しかし、特にAIが得意とする論理的に込み入った会話では、論点Aから生じた論点B、論点Cに対してそれぞれ別々の会話ブランチを伸ばしていきたいことがある。もちろん私は同時に1つの文章しか入力できないから並列性を上げられるわけではないが、あとから見直して話の筋がわかりやすくなる。コンピュータ上で行う会話なら、そのように進化しても良いはずだ。

冷静にこれSlackのスレッドじゃん。

20250531 チキンケバブ丼

予告通り特に予定がなく、のんびり起きてDota2とかコードリーディングとかしていた。午後はジムで全身鍛えて、その後近所に開店したデパートを視察してきた。よく行くアリオとは雰囲気が違っていて、格式が高い。そして小さい土地に階数を増やして床面積を稼いでいるので良くも悪くも密集度が高く感じた。

さすがに初日は込みすぎて食事はできそうもなかったので松屋で『チキンケバブ丼』を食べた。これはかなり良くて、肉はボリュームがあって香ばしく、ソースもかなり本格派の味だ(僕にはちょっと辛すぎるけど)。肉の量はサービス精神なのかもしれないが、少し野菜に変えてくれてもいいぞ。

洗面台の下が水漏れしていたので応急処置(たらいの設置)をして状況を調べて管理会社に連絡した。

Dota2 7.39bでDazzleの不人気の方のfacetであるNothl Boonが微妙に強化された。正直これでもPoison Bloomを選ぶ人が多いだろうからあまり勝率には変化がないだろうけど、Nothl Boonで数戦試してみると案外悪くない。Nothl Boon選択時、Dazzleのスキルを受けた味方は1回ごとにWeaveのスタックが2つ溜まり、1スタックごとにDazzleから受けるヒールが7.5%上昇する。Weaveのスタックは先に乗るので、全てのShadow Waveは回復量が確定で15%増加することになる。それより前に別のShadow WaveかGraveが入っていればスタックは更に溜まっているので、ヒーリングに特化させると相当量のバーストヒールになる。

たとえばDEMOでLv30のTinkerのHPをギリギリまで削ってからLv30Dazzle(ヒール増加タレント+Holy Rocket)でGrave→Wave→Greaves→Holy Rocket(20スタック)とやると430+676+615で1721回復させることができた。

…と思ってたんだけど別にNothl Boonじゃなくても1485回復したわ。意味ねぇ〜。一応バーストヒール入れられるとGrave後に平然と戦闘復帰できる点は強いです。

まあここまでヒーリングに特化してもなあという感じはある。GreavesとHoly Rocketって合わせて7300Gかかってるので、それならGlimmer Cape + セプター買えるじゃん。セプターは敵に囲まれているときヒールが多重で入るようになるので結局回復量を上げる効果もあるし。Greavesのセルフディスペルが重要なゲーム(Silencerとかね)なら意味あるけどねえ。

てかNothl Boonのoverhealって活用してる人いるのかな?無いよりマシだろうけど…結局Poison Touchでレーニング番長やりましょうというヒーローデザインは変わってないので、それなら普通にPoison Bloom選択で良さそう。解散です。解散。

JavaScriptのyield*は移譲先ジェネレーターがreturnした値はyieldしない

本記事は https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/yield*#%E4%BE%8Byield* 式自体の値 に例示されているものを改めて考えつつ説明したものです。

https://github.com/susisu/tskaigi2025 のコードリーディングをしていて、ここがわからなくてしばらく詰まっていた。

it("ジェネレータから yield された Promise が fulfill されたらその位置から再開する", async () => {
  function* myFunc(): Comp<number> {
    const a = yield* waitFor(Promise.resolve(1));
    const b = yield* waitFor(Promise.resolve(2));
    return a + b;
  }
  const promise = run(myFunc());
  await expect(promise).resolves.toEqual(3);
});

JavaScriptには yield* という構文がある。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/yield*

これはジェネレーターの中で使い、別のiterableに制御を移譲するために使う。とてもわかりやすい例があったので引用させてもらう。
https://stackoverflow.com/questions/17491779/delegated-yield-yield-star-yield-in-generator-functions

function* someGenerator() {
    yield 0;
    yield [1, 2, 3];
    yield* [4, 5, 6];
}

for (v of someGenerator()) {
    console.log(v);
}
// 0, [1, 2, 3], 4, 5, 6

yield [1, 2, 3];[1, 2, 3] をそのままyieldする。一方で yield* [4, 5, 6];[4, 5, 6] がiterableなオブジェクトならば、その生成値を1つずつyieldする。移譲すると書いたが、someGeneratorの呼び出し側から見ると、内側の内側にあるiterable([4, 5, 6] のこと)を透過的に扱えるとも言えそうだ。

ところでジェネレーターの中ではyieldの他にreturnも使える。returnに到達すると値を返しつつジェネレーターは終了する。

const gen = function* () {
  yield 1;
  return 2;
}()

console.log(gen.next()); // { done: false, value: 1 }
console.log(gen.next()); // { done: true, value: 2 }
console.log(gen.next()); // { done: true, value: undefined }

では yield* で移譲されたジェネレーターがreturnしたとき何が起きるのか。僕はなんとなくreturnされた値も透過的に外側から触れると思っていたが、触れない。

const g_inner = function* () {
    yield 1;
    return 2;
}();

const g_outer = function* () {
    const a = yield* g_inner;
    yield 3;
    return 4;
}()

console.log(g_outer.next()); // { done: false, value: 1 }
console.log(g_outer.next()); // { done: false, value: 3 }
console.log(g_outer.next()); // { done: true, value: 4 }

なんとなく出てくる値は 1, 2, 3, 4 になりそうな気がしたが(俺だけか?)、正解は1, 3, 4だ。MDNに

yield* は式であり、文ではありません。そのため、値に評価されます。

とサラリと書かれている通り、g_innerのreturnで返される値はg_outerのaに束縛される。それだけで、g_outerの呼び出し側には返されない。

ここで逆に考えてみる。移譲先のreturnで停止すると仮定すると、どのような問題が起きるだろうか?

まず、yieldの機能として、呼び出し側からnextに引数を与えることで、値を受け渡すことができる。

const gen = (function* () {
  const a = yield 1;
  const b = yield 2;
  return a + b;
})();

console.log(gen.next());     // { value: 1, done: false }
console.log(gen.next(10)); // { value: 2, done: false }
console.log(gen.next(20)); // { value, 30, done: true}

初回呼び出し後にyield 1;で停止しているジェネレーターに対して10を与えて再開させ、次にyield 2;で停止しているジェネレーターに20を与えて再開させている。3回目のnext呼び出しの後はreturn a+bに到達して30を返して終了する。

これは yield* で移譲された内側のジェネレーターにも機能するのだが、returnで停止すると仮定してシミュレーションしてみる。

const g_inner = function* () {
    const a = yield 1; // aに10が入る
    return 2; // ここで停止すると仮定
}();

const g_outer = function* () {
    const x = yield* g_inner;
    yield 3;
    return 4;
}()

console.log(g_outer.next());     // { done: false, value: 1 }
console.log(g_outer.next(10)); // { done: false, value: 2 } が返ると仮定する
console.log(g_outer.next(20)); // { done: false, value: 3 } このnextに渡した20はどこへ…?
console.log(g_outer.next());     // { done: true, value: 4 }

return 2で停止すると仮定すると、そのときnextに渡された値はどこに行くのだろう?xにはg_innerから返された値が入ることになっているので行き場がない。捨てるしかない。そうなると、g_outerを利用する側は中でg_innerに処理を移譲しているなんて本来知る必要がないのに、それを知らないと何回目かのnextに渡した値が勝手に捨てられてしまうことになる。インターフェースとしての安定性が失われている。

そういう意味で、returnでは停止しないのは筋が通っている。割と非直感的だと思ったんだけど、MDNだとサラッと1行説明されて例が1つあるだけで終わるので難しかった。普段からジェネレーターを使ってる人(いますか?)には自明な動作なのかもしれない。

一般のジェネレーターにおいてreturnは停止して値を返すという点でyieldと同じように見えるが、その見方は間違っているのだろう。yieldが明確に停止を意図した機能である一方で、returnは終了だから結果的に停止に見えてしまっているだけで、その本質はただ値とともに処理を返すことだけだ。yield* のようにその後も外側のジェネレーターに制御を戻して続行できるのであれば停止しないように見えるのは必然なのだろう。