私が歌川です

@utgwkk が書いている

ソートアルゴリズムの可視化アニメーションが作りたくなって

はじめに

YouTubeを見ているとたまにこういう、ソートアルゴリズムの動作を可視化するアニメーション動画が流れてくる。

www.youtube.com

こういうのはHTML/JavaScriptだとどうやって作るんだっけな、ということで作ってみた。配列の要素が入れ替わるのに対応して、画面中のバーが入れ替わる、という形を目指す。

基本的なアイデア

配列に対してソートアルゴリズムを実行しつつ、並列してDOM要素や属性値を入れ替える、みたいなことを考えたくなるけど、普通にソートアルゴリズムを実装すると人間には見えない速さで終わってしまう。10万要素の配列を Array.prototype.sort メソッドでソートしたら、普通は1秒もかからず終わるだろう。

ソートを実行しつつ、配列の要素を入れ替えた直後にDOM要素を書き換える余地がほしい。言い換えると、配列の要素を入れ替えた瞬間にソートを中断し、DOM要素の入れ替えが終わるぐらいの時間待ってからソートを再開したい。どうやって?

結論から言うと、ジェネレータ関数を使うことで実現できる。ソートアルゴリズムをジェネレータ関数で実装しつつ、配列の要素を入れ替えた直後に yield 演算子でジェネレータの呼び出し元に処理を戻してきて、DOM要素を入れ替える、という形に整理する。setInterval 関数を使って適当な時間間隔ごとにジェネレータを進めて、ジェネレータが完了するまで回してあげればいい。

実装

雰囲気はだいたいこういう感じ。

// 入れ替え対象の配列を初期化する
let arr = arrayToShuffled(new Array(120).fill(0).map((_, i) => i));

function* swap(i: number, j: number) {
  [arr[i], arr[j]] = [arr[j], arr[i]];
  yield;
}

function* bubbleSort() {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length; j++) {
      if (arr[j] < arr[j + 1]) {
        yield* swap(j, j + 1);
      }
    }
  }
}

const gen = bubbleSort();
let swapCount = 0;
const timer = window.setInterval(() => {
  swapCount++;
  const { done } = gen.next();
  // ここでDOM要素を書き換える
  if (done) {
    console.log(`finished with ${swapCount} swaps`);
    window.clearInterval(timer);
  }
}, 1000 / 120);

yield* 演算子を使うことで他のジェネレータに処理を移譲できる。これによって swap 関数のようなヘルパーを作れるし、マージソートのような再帰を伴うアルゴリズムも実装しやすくなる。

できたもの

リポジトリはこれです。Reactを使いつつ無理やり useSyncExternalStore フックで値を同期しているのがかわいらしいポイントです。

github.com

https://sugarheart.utgw.net/sort-animation/ にアクセスしてクエリパラメータでいくらか調整できます。

  • n: 要素数
  • algorithn: ソートアルゴリズム。この記事を書いた時点では以下のいずれかに対応しています。デフォルトは bubble です
    • bubble: バブルソート
    • shaker: シェーカーソート
    • insertion 挿入ソート
    • merge: マージソート

動作例

Webページなのでiframeで埋め込めます。こうやって見るとバブルソートって遅いんですね。

バブルソート (N=200)

シェーカーソート (N=200)

挿入ソート (N=200)

マージソート (N=1000)

おわりに

ソートアルゴリズムの可視化アニメーションをどうやって作るのか、疑問が解決できたのでよかった。普段あまりジェネレータを使って暮らしておらず、Promiseによる非同期処理とはまた異なるパラダイムにちょっと足を踏み入れることができたんじゃないか。ジェネレータの中で再帰したいけどどうすれば……と思っていたら yield* 演算子で解決できることを知ったあたりでだいぶ前進した。

クイックソートを実装しようとしたけどうまく書けず断念したので、今後の課題とします。

いろいろなソートアルゴリズムを実装してランダムなスクリーンセイバーにしたいな~。

2025 🔜 2026

はじめに

2024年の振り返り記事はこっち。

blog.utgw.net

仕事

5月末でひと区切りがつき、6月から仕事内容が変わった。

技術的に変わったこととしては、Next.jsやTypeScriptバックエンドが主戦場になったのがいちばん大きいだろうか。何気にTypeScriptバックエンドに仕事で向き合うのは初めてではある。TypeScript自体は書いたことのある言語だけど、バックエンドっぽい実装をやるとか、フロントエンドとバックエンドの境界がどこにあるか・どこに設けるべきか考えるとか、そういう要素は新たに出てきている。バックエンドなしの (純粋にSSRするための) Next.jsだけ書いていたときも境界を意識する場面は多々あったけど、バックエンドが同居することによって強く意識せざるを得ない場面が増えたと思う。Next.jsについても、App RouterやServer Actionsを使うようになり、Server Actionsはまだ分かるけどApp Router時代のコンポーネント設計にまだ頭が追いついていない。

AIコーディングエージェントを本格的に業務利用するようにもなった。今のところはClineに指示を出して小さめの機能を作ってもらったり横展開してもらったりするぐらい。そもそも仕様が難しかったり、経緯を読み解いていくところから始まったりしていて、コーディング自体がボトルネックになりきれていない、という風に現状を捉えている。なので、AI技術によって作業効率が向上しているのかと聞かれるとあまり自信を持った答えは出せない。まだ人間がボトルネックすぎて底力を出せていないのか、ボトルネックが単に移動するだけなのか、実感を持った答えは出せていない。ただ、人間の側も鍛錬をする必要はまだあるだろう、というのが自分の読みだけど今後どうなっていくのか。

そういえばサマーインターンの講師をやった。Webにまつわる技術要素を俯瞰し、取っ掛かりを作ってもらうぐらいまではいけたんじゃないか。あとはオフラインで講義をやる地の利を活かして、周囲の社員から実務の話を引き出したりとかやってた。サマーインターンの講義を受ける側から教える側になる、という実績が解除されたことになる。

hatena.co.jp

あとは自分の技能を伸ばしていくことのほかに、知見を伝授していくこともそろそろ考えないといけないのか? とか思うことはいろいろある。飲みに行ったときに引っ張り出してください。

旅行

2025年の韓国旅行は2月末・7月・10月の3回だった。10月は一人海外旅行ということになったが、もはや何度も行ってるので訳は分かるという感じでもあった。一人メシに困ることだけがネック。同人誌即売会で本を買う以外のコミュニケーションもちょっとずつできるようになっているので、更に伸ばしたい。けど2026年上旬は今のところ日程がなかなか合わない……。

RubyKaigiで松山に、YAPCで福岡に行ったりもしている。東京以外の国内旅行の頻度が減っている気がするが、そのぶん韓国旅行に吸われているのかも?

韓国語

2年ぐらい韓国語を学んでいて、客観的な実力が分かるように検定でも受験してみようか、と思い立ってTOPIK Iに申し込み、2級に合格した。思ったよりも簡単だったのでTOPIK IIを申し込んでみようかなー。

blog.utgw.net

二次創作を吸う分には、あとは知らない単語・文法を埋めていけばだいたい読めるでしょう、ぐらいの実力にはなっていると思う。最近はキャラクターの口調に対応する韓国語の表現に関心が出てきており、いわゆるお嬢様言葉の「~ですわ」みたいな文末表現があることに気づいたけど、日本語でまとまった解説をしている文章を見つけられていない。

リスニングはなかなか鬼門で、TOPIKぐらいゆっくり話されるならギリギリいけるだろうけど、現実は厳しい。単語のカバレッジを高めつつ、シャドーイングとか真面目にやっていくしかないんだろうか。あとは韓国語が堪能な友だちを探すとか???? リスニングの問題がじゃんじゃん出てきて、正誤判定がすぐできる仕組みを用意するのが難しいんだよなー。Duolingoはある程度のところまで進むと対話形式のリスニングが出題されなくなるので物足りない。

そしてこれは記事を書いているときの気づきです。おま環なのかもしれない。

ゲーム

今年やったゲームのベストを選ぶとするなら、やはり「魔法少女ノ魔女裁判」(まのさば) になるだろうか。展開やキャラクターに引き込まれ続け、プレイした後も二次創作を追い求め続ける、これでは共犯者なのか「なれはて」なのか分からない。ブックオフに開店凸することにもなった。Acaciaの他のゲームはどうなっていくのか……。

blog.utgw.net

↓の記事はネタバレ満載なので、未プレイの方は注意。

blog.utgw.net

都市伝説解体センターもかなり刺さったと思う。ゲームの難易度自体はそれほど高くなく、順番にこなせばクリアできる構成になっているけど、最後でひっくり返されたまま終わった。こちらも続編がすっと出せるような構造じゃなさそうではある。

トリッカルのグローバル版がローンチしたので、ほどほどにやっている。好きなキャラクターが加入してくれたらいいぐらいのテンション。いついかなる場面でもほっぺたを引っ張れるという点においては、他に類を見ない画期的なゲームだと思う。あとはキャラクターの声を韓国語に切り替えるのも初めて見た。トリッカルは韓国語ボイスでやる方が、キャラクターの生意気さが増されていいと思う。先述したように、韓国語での口調と日本語訳をなんとなく対応づけてストーリーを読めるのもいい。ローカライズの都合だろうけど、もしかして台詞の順番が入れ替わってないか? というのも気づけるようにはなった。最近は新年のイベントストーリーがよくて、エピカの口調がかなり하오体混じりに聞こえるのに気づきながら読み進められた。

trickcal.biligames.com

ブルアカはストーリーが読めたらいい方という感じでのんびりやっている。こういうのばっかりですね。セイアちゃんが実装され、エデン条約編以降のアリウス分校の、アリウススクワッド以外の動向が明らかになり、あとなんか他にもいろいろあったはず。

Nintendo Switch 2は多言語版を買ったけど、スプラトゥーン3がさくさく動くようになったことに感動するぐらいしかできていない。まのさばのSwitch版が出たらどうせ買うだろうけど、しかしSwitch 2である必要がないな……。最新ゲーム機が家にあるほうが暮らしが豊かなはずなのでいいということにする。

同人活動

夏コミも冬コミも落選したので、出す側としては何もやってない!! 知り合いのサークルをちょっと手伝ったぐらい。東京ビッグサイトの工事の影響で収容スペース数が減っているのはあるだろうけど、なにか書いて出す機会がないならまあいいか、とモチベーションが下がりつつある。そして来年も東館の工事はありそうだった……。

※大規模改修工事により、以下のとおり施設の部分休館が発生いたします。
休館期間(予定)

東展示棟(1~3ホール):2025年7月から2026年3月末まで
東展示棟(4~6ホール):2026年4月から2026年12月末まで
全館:2027年1月から2027年2月中旬まで
   2028年1月から2028年2月中旬まで
利用照会(空き状況のお問合せ)|ご利用ガイド|東京ビッグサイト(東京国際展示場)

2025年も相変わらずブルアカの二次創作を吸っていたけど、下半期はそれに加えてまのさばやトリッカルも追うようになった。どちらも供給が少ない (コミケで油断したらすぐ売り切れている、という意味) ので今後どんどん厚くなるか期待しておく。

おわりに

相変わらず抱負はないです。あるとすれば「2026年も飲みに行く」ぐらい? よろしくお願いします。

韓国・ソウルで行ったクラフトビールの店

日本にいるときと変わらず、韓国・ソウルに行くたびに、どこかしらの店でクラフトビールを飲んでいる。あんまり韓国にクラフトビールのイメージはなかったけど、探すといくらか見つかるものである。

ソウルで行った店をいくつかピックアップしてみることにする。ビールの味に対する言及はない。ホームページっぽいものを探してリンクを貼ってみてはいるけど、どこにリンクしているかはまちまち。

行った店

The Ranch Brewing Euljiro

www.bestpizzaseoul.com

ホームページのドメインが非常に強い。入口のドアが自動販売機みたいな形になっているので、初見でどうやって入るのか分からなかった。

乙支路3街でとりあえずクラフトビールを飲むなら、ここかMagpie Brewingあたりが鉄板なんじゃないか。

Euljiro Brewing

www.konest.com

The Ranch Brewing Euljiroの近くにある。店内はブラックライトの光に包まれているので、パスポートとかが光りまくると評判。

Indie Beer Factory

untappd.com

西大門あたりにある。店内はそれなりにスペースがあるけど、2024/7に初めて行ったときは満席で入れなかった。

Magpie Brewing NEAR MINT+ EULJIRO

www.magpiebrewing.com

服屋を突っ切って階段を上ると入れる。雰囲気がけっこう好き。

Tap Public Yeouido

量り売り形式で、自分でビールを注いで飲める。韓国のビールがそこそこありつつ、海外のビールや日本のアサヒスーパードライなんかも入っていたと思う。気軽に入って飲みつつ休憩するのにちょうどいい。

www.jbja.jp

The Hand and Malt Brew Lab Yongsan

sorademo.com

新龍山駅からすぐのところにある。龍山公園の近くで、2階でしんみり飲んでいた。

Daily Beer Hongdae Dongkyodong (생활맥주)

dailybeer.co.kr

弘大に来たらだいたいここに行ってる。500mLのフラスコでがぶがぶビールを飲んでいる人がだいたいいる。そんな中で飲み比べを飲む。

ちなみに弘大には鳥貴族もある。わざわざソウルまで来て鳥貴族に行くのもおもしろい気がするのでいつか行っておきたい。

おわりに

いくつかピックアップしてみて思うに、やはりあんまり江南を開拓できていない。次回は江南のいい店を探したいなー。

取り上げられていない店があるような気がするので、一緒に旅行に行ったことがある皆様、誰かあとでこっそり教えてください。あと他におすすめのビール屋があったらそれも教えてください。

あなたの手元のGoプロダクトにひっそり佇んでいるコード片のことを、ほんの少しでいいから思い出してみませんか

はじめに

これははてなエンジニア Advent Calendar 2025 18日目の記事です。昨日は id:kouki_dan毎日1コミットを続けて1年が経った - Lento con forzaでした。

id:utgwkk です。Goを書いていて、頻出のイディオムをもっと短く書きたい! という欲求に駆られることがあると思います。そのような場面に出くわした人は、プロダクト内にちょっとしたコードを書いて便利に使うようになります。

今日の記事は、そうやってプロダクトのあちこちに登場する、ともすれば脚光を浴びることのないかもしれないコード片たちのことを思い出す、という趣旨になっています。

func Must[T any](T, error) T

func Must[T any](x T, err error) T {
  if err != nil {
    panic(err)
  }
  return x
}

返り値の型が (T, error) のようにエラーを返しうる関数 f があったとして、Must(f()) のように呼び出すことで、エラー時の条件分岐を省略できます。エラーが発生したらpanicします。

テストコードや、アプリケーションの起動時に一度だけ実行する初期化処理などではよく使います。html/templateなど、標準ライブラリでも使われているイディオムです*1。Go 1.18でジェネリクスが導入された*2ことによって、同様のイディオムが1つの関数で (型ごとに関数定義を分けることなく) 実現できるようになりました。

エラーが返る場合の条件分岐を無視して値を返すことで、メソッドチェーンなどを書きやすくなるため、テストコードでは重宝します。もちろん、普通は乱暴にpanicするのを避けて、ちゃんとエラーをチェックするべきです。

Goのエラーハンドリングについては何かと話題になりがちですが、2025/12/17現在、Goチームは当面の間エラーハンドリングにまつわる文法の変更を停止する、と表明しています。

For the foreseeable future, the Go team will stop pursuing syntactic language changes for error handling. We will also close all open and incoming proposals that concern themselves primarily with the syntax of error handling, without further investigation.
[ On | No ] syntactic support for error handling - The Go Programming Language

func ReferenceOf[T any](T) *T

func ReferenceOf[T any](t T) *T {
  return &t
}

渡した引数に対する参照を返します。たとえば、optionalなint型という気持ちで *int 型 (intのポインタ) を渡す場面があったとして、普通に書くとint型の変数を定義した上でその参照を取ることになると思います。ReferenceOf 関数を導入することで、これが1行で書けるようになります。

// before
x := 1
f(&x)

// after
f(ReferenceOf(1))

このイディオムはaws-sdk-go V2でも形を変えてよく使われています。API呼び出しのパラメータを渡すために aws.String のような関数を使ったことがあるかもしれませんが、まさに与えられた文字列の参照を返すような実装になっています*3

ちなみに、Go 1.26では組み込み関数の new を使うことで、同様のことが実現できるようになる予定です*4。つまり ReferenceOf 関数は、プロダクトのGoのバージョンが1.26になったら引退してもらえる可能性が高いということですね。今まで本当にありがとう……。

おわりに

あなたの手元のGoプロダクトにひっそり佇んでいるコード片のことを、ほんの少しでいいから思い出してみませんか? 書くコードを少なくして本質に向き合うきっかけや、はたまたGo標準の言語機能で代替されうるものなど、そこにはいろいろなドラマがあると思います。

小粒な記事でしたが、この記事が「ちょっとしたコード」について思いを馳せるきっかけになれば幸いです。

明日の記事は id:r4wxii【Jetpack Compose】PathMeasureを使ったPathのアニメーション - No one knows unknownsです。

時間のないサイト運営者リング