動かざることバグの如し

近づきたいよ 君の理想に

新ルールに対応した証券コードの正規表現

環境

  • JavaScript

やりたいこと

2024年1月以降に新規上場した銘柄から、証券コードに英文字が混じるようになった。

従来は「数字4桁」という単純なルールだったので、バリデーションも /^\d{4}$/ で事足りていた。しかし新ルール対応が必要になったので、旧来の数字4桁コードと新しい英数字混在コードの両方にマッチする正規表現が必要となるのでメモ。

コード

まず新しい証券コードのルールを整理する。

www.jpx.co.jp

  • 4桁構成は変わらない
  • 1桁目と3桁目は必ず数字
  • 2桁目と4桁目は「数字」または「英大文字」
  • 英大文字は数字と見間違えやすい B、E、I、O、Q、V、Z の7文字を除いた19文字(A, C, D, F, G, H, J, K, L, M, N, P, R, S, T, U, W, X, Y)のみ使用可

このルールに対応した正規表現がこれだ。

^[1-9][0-9ACDFGHJKLMNPRSTUWXY][0-9][0-9ACDFGHJKLMNPRSTUWXY]$

各パートの意味はこうなる。

  • ^[1-9] : 証券コードは1300番台から始まるため1桁目は1〜9の数字のみ。0始まりは存在しない
  • [0-9ACDFGHJKLMNPRSTUWXY] : 2桁目と4桁目。数字か許可された英大文字のどちらか
  • [0-9] : 3桁目は従来通り必ず数字

JavaScriptで使う場合はこうなる。

const stockCodeRegex = /^[1-9][0-9ACDFGHJKLMNPRSTUWXY][0-9][0-9ACDFGHJKLMNPRSTUWXY]$/;

// 旧来の数字4桁コード
console.log(stockCodeRegex.test('7203')); // true (トヨタ)

// 新形式の英数字混在コード
console.log(stockCodeRegex.test('456A')); // true (HUMAN MADE)

// 無効なコード(禁止文字Iを含む)
console.log(stockCodeRegex.test('2I3C')); // false

なお、証券会社のアプリや検索フォームで小文字入力も受け付けたい場合は i フラグを付けるか、文字クラスに小文字も追加すればいい。

// iフラグで大文字小文字を無視する場合
const stockCodeRegexCI = /^[1-9][0-9ACDFGHJKLMNPRSTUWXY][0-9][0-9ACDFGHJKLMNPRSTUWXY]$/i;

console.log(stockCodeRegexCI.test('2a3c')); // true

参考URL

Mac Studio M5 Ultra欲しすぎてスペック予想した

Mac Studio M5 Ultra欲しい

Mac Studio M5 Ultraが2026年秋に発売予定らしい。

前モデルのM3 Ultraは最大512GBという常軌を逸したメモリ容量が売りだった。筆者もローカルで100Bパラメータ超えのLLMを動かしたいのだが、容量の問題よりも速度がネックになっている。

問題はメモリ帯域幅だ。M3 Ultraのメモリ帯域幅は819.2 GB/sで、一見速そうに見える。しかしGeForce RTX 4090が1,008 GB/s、RTX 5090に至っては1,792 GB/sを叩き出しており、M3 Ultraはそれらに大きく水をあけられている状態だ。

LLM推論のスループットはメモリ帯域幅にほぼ律速されるため、メモリ容量がいくら大きくてもトークン生成速度が遅ければ実用にならない。M5 Ultraがこの帯域幅の壁をどう打ち破るのかを見ていきたい。

どうやってスペック予想するのか

予測の基本的な手法は「MaxからUltraへのスケーリング法則」を利用することだ。

AppleはこれまでUltraチップを、2つのMaxダイをシリコンインターポーザ(UltraFusion)で結合して作ってきた。そのためコア数、メモリ容量、メモリ帯域幅は基本的にMaxの「2倍」になる。この法則はM1世代から一貫して守られており、予測精度はかなり高い。

もう一つの手法が「実測ベンチマークからの外挿」だ。M5 Maxはすでに市場に出回っているため、Geekbench 6などの実測値が蓄積されている。これをUltraのスケーリング効率(M3世代では約70%)を考慮して計算することで、推測ではなく実データに基づいた予測ができる。

ただしM5世代はアーキテクチャが大きく変わっており、Fusion Architecture(SoIC-MH 2.5Dパッケージング)の導入によってダイ間通信レイテンシが改善されているため、スケーリング効率は従来の70%より高い約90%になると予測されている。M3 UltraでGPUのスケーリングが70%止まりだったのは、UltraFusionのダイ間通信がボトルネックになっていたからで、その制約がSoICで解消されるという読みだ。

スペック予想表

項目 M3 Max(実測値) M3 Ultra(実測値) M5 Max(実測値) M5 Ultra(予測)
CPU コア数 16 32 18 36
- Superコア - - 6 12
- Performanceコア 12 24 12 24
- Efficiencyコア 4 8 0(廃止) 0(廃止)
GPU コア数 40 80 40 80
Neural Engine数 16 32 16 32
ユニファイドメモリ(GB) 128 512 128 256
メモリ帯域幅(GB/s) 400 819.2 614.4 1,228
Geekbench 6 マルチコア 21,385 28,169 29,233 42,864
Geekbench 6 Metal(GPU) 155,991 251,466 257,960 362,000

表から読み取れる最大の変化はメモリ帯域幅だ。M3 Ultraの819.2 GB/sに対し、M5 Ultraは約1,228 GB/sと約1.5倍の帯域幅を持つ見込みだ。前述のRTX 5090(1,792 GB/s)にはまだ届かないが、RTX 4090(1,008 GB/s)は大きく上回る水準になる。

GPU性能(Geekbench Metal)も注目だ。M5 Maxはすでにコア数が2倍であるM3 Ultraを単体で上回るスコアを記録している。これはFusion ArchitectureとNeural Acceleratorの統合がコア数の物理的差異を覆すほどの効率をもたらした結果だ。M5 Ultraではそこからさらにスケールアップが見込まれる。

CPUマルチコアについては、M5世代でEfficiencyコアが廃止されているにもかかわらずスコアが伸びているのが面白い。SuperコアとPerformanceコアのみで構成されることで、コア密度あたりの演算性能が純粋に向上している。また注目すべきは、M5 MaxのマルチコアスコアがM3 Ultraを上回っている点で、これはノートPC向けチップがデスクトップ向けフラッグシップを抜いたことを意味する。

備考

本記事の予測値はAppleの公式発表ではなく、M5 Maxの実測データとApple Siliconのスケーリング傾向をもとにした推計値だ。実際の製品スペックとは異なる可能性がある。

特にメモリ容量については、LPDDR5X-9600モジュールの世界的な供給逼迫の影響で変動し得る。最高構成が256GBにとどまる可能性もある点は留意しておきたい。価格についても、SoICパッケージングのコスト上昇を反映して前世代より引き上げられる見込みで、ベースモデルで5,000ドル台になるとの見方が多い。

バスタブクレンジングの銀イオンプラスとハードの違いを調べてみた

バスタブクレンジング種類多すぎ問題

普段の浴槽掃除はほぼ毎日ルックプラス バスタブクレンジングに任せている。 今までは「銀イオンプラス」を買い続けていたのだが、在庫が減ってきたのでAmazonを徘徊していたら見慣れない「ハード洗浄」が増えていた。

プレスリリースも読んでみたがよくわからん。。従来の「こすらず洗える」路線はそのままに、頑固な汚れまでカバーしたいって立ち位置なのか。。?

www.lion.co.jp

『ルックプラス』は従来の掃除方法ではなく、独自の新方式を採用して「がんばらなくてもキレイ」を実現するお掃除習慣を提案してきました。
中でも『ルックプラス バスタブクレンジング』は、「ミストを浴そう全体に吹きかけて、60秒後に流すだけ。」という浴そうを「こすらず洗える」新方式で、2018年の発売以来、ご高評をいただいております。
これにより、浴そうをこすらず洗える洗剤の購入率は浴室洗剤市場の約4割※1まで拡大しました。
一方、従来の「こすって洗う」方法を続ける人の多くは、湯抜き後の「長時間放置したときの汚れ」や「入浴人数が多い家庭の汚れ」について、「こすらないと落ちない」と認識していることが明らかになりました※2。
そして実際に、これらの汚れは頑固になっていることが判明しました。
そこで、浴室の頑固汚れまでこすらずに落とし、従来の負担が大きい掃除方法から解放する新アイテムとして『ルックプラス バスタブクレンジングHARD洗浄』を発売いたします。

実店舗で成分表示を見比べてみた。

ルックプラス バスタブクレンジング 銀イオンプラスの成分

今まで使ってたやつ。青いパッケージ

picture 0

  • アルファオレフィンスルホン酸ナトリウム 3%
  • 金属封鎖剤
  • 溶剤
  • pH調整剤

主役は「アルファオレフィンスルホン酸ナトリウム」で、いわゆるアニオン系の界面活性剤である。洗浄力が高く、泡立ちも出やすいタイプなので、「洗剤らしい効き方」をする成分だ。

雑に言えば、しっかり汚れをはがしにいく方向の配合に見える。皮脂汚れや石けん由来の汚れに強く、泡で洗っている感触も出しやすい。 一方で、泡が立つということは、そのぶん流す水量や時間が少し増えやすい。 銀イオンの名前から除菌寄りの製品に見えるが、成分表の主役はあくまで界面活性剤である。 日々の浴槽掃除の観点では、「泡立ちと洗浄感がわかりやすい従来型」に近いと考えたほうが実態に合っている。

ルックプラス バスタブクレンジング ハード洗浄の成分

新発売のやつ。黒いパッケージ

picture 1

  • ポリオキシエチレンアルキルエーテル 4%
  • 金属封鎖剤
  • 溶剤
  • pH調整剤

こちらの主成分は「ポリオキシエチレンアルキルエーテル」で、非イオン系の界面活性剤である。名前は長いが、特徴はわかりやすくて、皮脂やぬめりのような柔らかい汚れに浸透して浮かせるのが得意なタイプだ。

非イオン系は泡立ちが比較的おとなしい。その代わり、浴槽全体へ広がりやすく、すすぎも軽く済みやすい。つまり「こすらず、スプレーして、時間を置いて、流す」というバスタブクレンジングの使い方とはかなり相性がいい。

どのあたりがハードなのかは結局よくわからないが、、

それぞれのメリットとデメリット

成分名だけ見ても感覚的には掴みにくいので、製品名を伏せたうえで複数のAIに比較させてみた。

まず共通していたのは、毎日のお風呂上がりにスプレーして流す用途ならハード洗浄側の配合が有利だという点である。理由もほぼ同じで、皮脂やぬめりへの浸透力、泡切れ、すすぎやすさが評価されていた。

逆に銀イオンプラス側は、洗浄感の強さや泡立ちのわかりやすさが長所として挙げられていた。しっかり洗った感じが欲しい、少し重めの汚れも日常掃除の延長で押し切りたい、という場面ではこちらの性格が合う。

要するにこんな整理になる。

  • 銀イオンプラス
    • 泡立ちがあり、洗っている感触が出やすい
    • 日常汚れに対する洗浄力の体感は強め
    • すすぎはやや重くなりやすい
  • ハード洗浄
    • 皮脂やぬめりのような日常汚れを、こすらず浮かせるのが得意
    • 泡が暴れにくく、シャワーで流しやすい
    • 毎日回す運用に向いている

一方で、どのAIも「白い水垢や長く放置して固まった汚れは別問題」としていた。浴槽のザラつきや白っぽい固着汚れは、界面活性剤だけでどうにかする領域ではない。ここはハード洗浄でも銀イオンプラスでも万能ではなく、必要なら酸性寄りの別洗剤を出すしかない。

結局どっちがいいんだ?

ものは試しということで今回はハード洗浄を買った。しばらく使ってみて、「ハード」じゃなかったら素直に銀イオンプラスに戻る。以上。

Gitで別ブランチの特定ファイルをサルベージしたい

やりたいこと

別ブランチにある特定ファイルだけを、いまの作業ツリーへ引っ張ってきたいことがある。

git restore --source <branch> でも近いことはできるが、雑に叩くとインデックスや手元の変更まで意識する必要があって地味に気を使う。欲しいのは「指定ブランチから指定ファイルだけ抜き出して、必要なら既存ファイルを退避してから上書きする」くらいの単機能なやつである。

その場で毎回 git show branch:path > file を組み立ててもよいが、絶対パスと相対パスの差分吸収やバックアップ作成まで含めるとシェルスクリプトにしておいた方が楽だった。

コード

以下を ~/.local/bin/git-salvage.sh に作成する。chmod +x ~/.local/bin/git-salvage.sh も実行する。

#!/usr/bin/env bash
set -euo pipefail

usage() {
  echo "Usage: $(basename "$0") <branch> <file-path>" >&2
  echo "  branch     : サルベージ元のブランチ名" >&2
  echo "  file-path  : リポジトリルートからの相対パス、または作業ツリー内の絶対パス" >&2
  exit 1
}

[[ $# -ne 2 ]] && usage

BRANCH="$1"
FILE_ARG="$2"

# Git リポジトリのルートを特定
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || {
  echo "エラー: Git リポジトリ内で実行してください" >&2
  exit 1
}

# 絶対パスが渡された場合はリポジトリルートからの相対パスに変換
if [[ "$FILE_ARG" = /* ]]; then
  REL_PATH="${FILE_ARG#"$GIT_ROOT/"}"
else
  REL_PATH="$FILE_ARG"
fi

# ブランチの存在確認
git rev-parse --verify "refs/heads/$BRANCH" > /dev/null 2>&1 || \
git rev-parse --verify "refs/remotes/$BRANCH" > /dev/null 2>&1 || {
  echo "エラー: ブランチ '$BRANCH' が見つかりません" >&2
  exit 1
}

# 対象ブランチにファイルが存在するか確認
git cat-file -e "$BRANCH:$REL_PATH" 2>/dev/null || {
  echo "エラー: ブランチ '$BRANCH' にファイル '$REL_PATH' が存在しません" >&2
  exit 1
}

# 書き込み先(作業ツリー上の絶対パス)
DEST="$GIT_ROOT/$REL_PATH"

# 書き込み先ディレクトリを作成
mkdir -p "$(dirname "$DEST")"

# 既存ファイルがあればバックアップ
if [[ -f "$DEST" ]]; then
  BACKUP="${DEST}.bak.$(date +%Y%m%d%H%M%S)"
  cp "$DEST" "$BACKUP"
  echo "バックアップ作成: $BACKUP"
fi

# ブランチのファイルを展開(インデックスは変更しない)
git show "$BRANCH:$REL_PATH" > "$DEST"

echo "完了: '$BRANCH:$REL_PATH' -> $DEST"

実行例

❯ git-salvage.sh 
Usage: git-salvage.sh <branch> <file-path>
  branch     : サルベージ元のブランチ名
  file-path  : リポジトリルートからの相対パス、または作業ツリー内の絶対パス
❯ git-salvage.sh origin/main src/server/app.ts
バックアップ作成: /home/thr3a/work/example/src/server/app.ts.bak.20260521003214
完了: 'origin/main:src/server/app.ts' -> /home/thr3a/work/example/src/server/app.ts

origin/main 上の src/server/app.ts を現在の作業ツリーへ復元している例である。対象ファイルがすでに存在していれば、タイムスタンプ付きで .bak を切ってから上書きする。

Lubuntu26.04でxrdpリモートデスクトップ

環境

  • Lubuntu 26.04

やりたいこと

Lubuntuのマシンへリモートデスクトップをしたい。Ubuntu 26.04からX11が廃止されてWaylandになったためxrdpではなくGNOME Remote Desktopを使用したが、 LubuntuはGNOMEではないため逆にGNOME Remote Desktopが使えない。そこで今までどおりのxrdpを使用する。

blog.turai.work

インストール

apt install xrdp

そのあとにサーバー再起動する。

接続できない

インストール自体はこれで終わるのだが、そのままではRDP接続できなかった。Windowsのリモートデスクトップクライアントから接続してログインまでは通るものの、デスクトップが表示される前に即切断される。

systemctl status xrdp xrdp-sesman でサービス状態を見ると両方とも起動している。つまり xrdp 自体が死んでいるわけではない。

その状態でログを見ると、以下のようなエラーが出ていた。

[ERROR] Xorg server closed connection

さらに /var/log/xrdp-sesman.log を確認すると、LXQtのセッションが起動直後に終了していることが分かる。

[INFO ] Window manager (pid 2290, display 10) finished normally in 0 secs
[WARN ] Window manager (pid 2290, display 10) exited quickly (0 secs).
        This could indicate a window manager config problem
[INFO ] Session on display 10 has finished.

最初は Xorg 側の問題かと思ったが、~/.xorgxrdp.10.log では正常終了扱いだった。つまり落ちていたのは Xorg ではなく、その上で起動する LXQt セッション側である。

直接の症状は「認証後すぐ切断される」である。画面が真っ黒なまま落ちるので分かりにくいが、実際には lxqt-session が起動してすぐ死んでいる。

決定打になったのは ~/.xsession-errors の中身だった。

isPrimaryInstance
XIO: fatal IO error 17 (File exists) on X server ":0"

isPrimaryInstancelxqt-session のシングルインスタンス判定まわりの出力で、すでに同じユーザーで LXQt セッションが動いていることを示している。

実際に確認すると、サーバー側ではローカルログイン済みの LXQt セッションが存在していた。

pgrep -a lxqt-session
loginctl list-sessions

Lubuntuをデスクトップ用途でも使っていると、物理コンソール側でログインしたままRDP接続したくなることがある。今回はまさにその構成でハマった。

原因

原因は、ローカルの LXQt セッションと xrdp 経由の LXQt セッションが同じ D-Bus セッションバスを見に行っていたことだった。

/etc/xrdp/startwm.sh はデフォルトだと /etc/X11/Xsession を実行するだけなので、環境によっては既存の DBUS_SESSION_BUS_ADDRESS をそのまま引き継ぐ。 その状態で startlxqt が起動すると、すでにローカル側で動いている lxqt-session を検出して「自分は2個目のインスタンスだ」と判断し、即終了する。

要するに xrdp が悪いというより、LXQt のシングルインスタンス前提と D-Bus 共有が噛み合っていなかった。

対処法

対処は単純で、RDPセッション専用の D-Bus セッションを作ってから LXQt を起動すればよい。

/etc/xrdp/startwm.sh を以下のように修正する。

変更前

test -x /etc/X11/Xsession && exec /etc/X11/Xsession
exec /bin/sh /etc/X11/Xsession

変更後

unset DBUS_SESSION_BUS_ADDRESS
exec dbus-launch --exit-with-session startlxqt

やっていることは3つだけである。

  • unset DBUS_SESSION_BUS_ADDRESS で既存のユーザー D-Bus バス参照を消す
  • dbus-launch --exit-with-session でRDPセッション専用の D-Bus を新しく作る
  • startlxqt をその独立したセッション上で起動する

これでローカルログイン中の LXQt セッションとは別物として扱われるため、lxqt-session の競合が起きなくなる。

修正後は次回接続からそのまま反映された。少なくとも手元の環境では systemctl restart xrdp は不要だった。

Lubuntuをヘッドレスで使うなら遭遇しないかもしれないが、ローカル画面とRDPを併用するならかなり踏みやすい罠だと思う。