**Unix `find`式のバイトコード化**

* **目的**  
  複雑な検索条件を、`find` ユーティリティが効率的に評価できるコンパクトで実行可能な形に変換すること。

* **主な構成要素**  
  - **演算子**:論理(`-and`、`-or`、`!`)、比較(`-eq`、`-ne`、`-lt`、`-gt` 等)  
  - **オペランド**:ファイル属性(名前・サイズ・種類)、タイムスタンプ、パーミッション、所有者、内容チェックなど  

* **コンパイル手順**  
  1. ユーザーが入力した式を抽象構文木(AST)に解析する。  
  2. AST をバイトコード命令列へ変換する。  
  3. 定数の折りたたみや不要な分岐の簡略化で最適化する。  

* **バイトコード形式**  
  - 各命令は固定長レコード:opcode + オペランド。  
  - opcode は `CHECK_NAME`、`COMPARE_SIZE`、`JUMP_IF_FALSE` 等を表す。  
  - オペランドは即値、文字列リテラル、または制御フロー用オフセットになる。  

* **実行**  
  `find` エンジンは検索ツリー内の各ファイルに対してバイトコードを解釈し、小さな仮想マシンでブール結果スタックを更新する。

* **メリット**  
  - 各ファイルごとに式を再解析するより高速。  
  - 完全な AST をメモリに保持するよりも記憶域が節約できる。  

* **典型的な使用例**  
  ```bash
  find /path -type f -size +1M -exec gzip {} \;
  ```
  `-type f -size +1M` の式は実行前にバイトコードへコンパイルされる。

2025/12/26 21:35

**Unix `find`式のバイトコード化** * **目的** 複雑な検索条件を、`find` ユーティリティが効率的に評価できるコンパクトで実行可能な形に変換すること。 * **主な構成要素** - **演算子**:論理(`-and`、`-or`、`!`)、比較(`-eq`、`-ne`、`-lt`、`-gt` 等) - **オペランド**:ファイル属性(名前・サイズ・種類)、タイムスタンプ、パーミッション、所有者、内容チェックなど * **コンパイル手順** 1. ユーザーが入力した式を抽象構文木(AST)に解析する。 2. AST をバイトコード命令列へ変換する。 3. 定数の折りたたみや不要な分岐の簡略化で最適化する。 * **バイトコード形式** - 各命令は固定長レコード:opcode + オペランド。 - opcode は `CHECK_NAME`、`COMPARE_SIZE`、`JUMP_IF_FALSE` 等を表す。 - オペランドは即値、文字列リテラル、または制御フロー用オフセットになる。 * **実行** `find` エンジンは検索ツリー内の各ファイルに対してバイトコードを解釈し、小さな仮想マシンでブール結果スタックを更新する。 * **メリット** - 各ファイルごとに式を再解析するより高速。 - 完全な AST をメモリに保持するよりも記憶域が節約できる。 * **典型的な使用例** ```bash find /path -type f -size +1M -exec gzip {} \; ``` `-type f -size +1M` の式は実行前にバイトコードへコンパイルされる。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:


Summary

この記事では、Unix の

find
コマンド式を効率的で単一レジスタの機械語に変換するバイトコードコンパイラ findc を紹介しています。
実行時に各式を解釈する代わりに、findc はまずシェンティング・ヤードアルゴリズムを用いて中置記法を後置記法へ変換し、次に 5 つの単純なオペコード(
halt
not
braf
brat
、および
action
)を発行します。

find
式にはループが含まれないため、すべての分岐は前方のみ相対ジャンプになります。コンパイラは fragment stack を使用します ― 各フラグメントはバイトコード命令列であり、ブランチターゲットを再計算せずにコードブロックを連結できます。この設計により、論理演算子(
-a
-o
、単項
!
)を処理し、明示的な副作用アクションがない式は自動的にデフォルトの
-print
でラップされます。

ピープルオプティマイザは冗長命令(二重否定や不要ジャンプなど)を削除してバイトコードをコンパクトに保ちます。
記事では、生成されたバイトコードの具体例(例えば

$ findc
action -print; halt
$ findc -type f -executable
→ 一連の
action
braf
、および
halt
命令)を提示しています。

オンライン WebAssembly デモでは、ユーザーが式がどのようにコンパイルされるかをインタラクティブに確認でき、オプティマイザの動作も見ることができます。式をバイトコードへ事前コンパイルすることで、findc は従来の木構造走査型解釈子より高速な実行と低メモリ使用量を提供し、他のコマンドラインツールやスクリプト環境で同様の手法を採用するインスピレーションになる可能性があります。

本文

2025年12月23日 – nullprogram.com/blog/2025/12/23/
(著者は現在米国での雇用機会に対してオープンです。)


はじめに

将来のプロジェクトに備えて、Unix の

find
ユーティリティについて考えていました。
find
はファイルシステム階層を対象にし、専用の式言語で選択した基本操作を 組み合わせてフィルタリングします。ユーザーは単項演算子と二項演算子を使い、 優先順位を付けるために括弧でグループ化して式を構築します。

find
は多くのファイルに対して式を適用する可能性があるため、
できるだけ前もって式をコンパイルし、バイトコードへ変換し、実行時の 要素ごとの処理量を最小限に抑えることは賢明です。
思案した結果、期待よりも単純な手法が見つかり、結果に満足しています。
後になって驚いたのは、すべての実際の
find
実装が木走査型インタプリタを 使用していることです。本記事では私のコンパイラがどのように動作するか、 実行可能な例と改善案をまとめています。


簡易概要

$ find [-H|-L] path... [expression...]

技術的には少なくとも 1 つのパスは必要ですが、ほとんどの実装では パスが指定されない場合に

.
(現在のディレクトリ)を暗黙的に使用します。
式が与えられなければデフォルトで
-print
が適用されます。
例えば次のようにすべてのファイルとディレクトリを表示します。

$ find .

これにより現在のディレクトリ以下の全ツリー(ディレクトリも含む)が 出力されます。

ファイルのみを表示したい場合

$ find . -type f -a -print

ここで

-a
は論理 AND(二項演算子)です。
-print
は常に真となるため、実際には
-a
を書く必要はありません。
隣接した操作は暗黙的に
-a
で結合されます。

ファイルを連鎖させてさらに条件を付ける例:

$ find . -type f -executable -print

-exec
-ok
-print
(または
-print0
-delete
などの副作用付き拡張)が 含まれていない場合、全体の式は暗黙的に
( expr ) -print
に包まれます。
したがって次のように書くこともできます。

$ find . -type f -executable

論理 OR の使用

$ find . -type f \( -executable -o -name '*.exe' \)

-o
は論理 OR(二項演算子)です。
ここで
\( … \)
を使う理由は、
-o
-a
より優先順位が低く、 また括弧自体がシェルのメタ文字であるためにエスケープが必要だからです。 (
[
]
を代わりに使用していない点は残念です。)
単項否定演算子
!
もあります。

$ find . -type f ! -executable

二項演算子は短絡評価されます。例えば次のように書くと:

$ find -type d -a -exec du -sh {} +

ディレクトリでない場合、左辺が偽になるため右辺(

-exec
)は 評価されません。等価な OR 版もあります。

$ find ! -type d -o -exec du -sh {} +

正規表現の扱い

GNU、BSD、BusyBox のすべての実装には

-regex
拡張があり、 式が評価される前に正規表現を即座にコンパイルします。

$ find . -print -o -regex [
find: bad regex '[': Invalid regular expression

これは「第 1 式が真なら第 2 式は評価しない」という元の精神と合わず、 私には驚きでした。再コンパイルを遅延させ、実際に使用されたときだけエラーにする設計も考えられます。


バイトコード設計

バイトコードインタプリタは 1 ビットのレジスタ(結果)だけを追跡すればよいので、 単一レジスタマシンとして実装できます。私が考案したオペコードは次の 5 種類です。

halt                // プログラム停止
not                 // レジスタの値を否定
braf LABEL          // false のときジャンプ(branch if false)
brat LABEL          // true のときジャンプ(branch if true)
action NAME [ARGS]  // アクション実行(例:-name, -type)
  • halt
    は明示的に終了を指定できるので便利です。
  • braf
    brat
    を使って
    -a
    -o
    を表現します。
    実際にはループは存在せず、ジャンプは常に前方(forward)になります。

実装では各アクション(

-name
,
-ok
,
-print
,
-type
等)が 専用オペコードを持つべきですが、ここでは汎用の
action
を使って 外観で判定するようにしています。
それぞれのアクションはレジスタを設定し、
-print
などは常に真にします。

このコンパイラを findc(“find compiler”)と呼びます。

更新情報:Wasm を通じてオンラインデモを試してみてください!
このバージョンには記事公開後に書いたピープルオプティマイザが含まれています。


push
Slice
マクロのため、非常に新しい C コンパイラ(例:GCC 15 または Clang 22)が必要です。
いくつかの
find
コマンドを試し、バイトコードがどのようになるか確認できます。

最も単純なケース

$ findc
// path: .
        action  -print
        halt

少し複雑に

$ findc -type f -executable
// path: .
        action  -type f
        braf    L1
        action  -executable
L1:     braf    L2
        action  -print
L2:     halt

ここで、パスがファイルでない場合に残りのプログラムをスキップするよう、 二番目のブランチ(

braf
)が機能します。
しかし既に改善余地があります。

        action  -type f
        braf    L1
        action  -executable
        braf    L1
        action  -print
L1:     halt

より複雑な例

$ findc -type f \( -executable -o -name '*.exe' \)
// path: .
        action  -type f
        braf    L1
        action  -executable
        brat    L1
        action  -name *.exe
L1:     braf    L2
        action  -print
L2:     halt

括弧内で

-executable
が成功すると右辺(
-name
)をスキップします。
brat
は直ちに
braf
にジャンプし、さらに前方へ移動させるとより良いです。

        action  -type f
        braf    L2
        action  -executable
        brat    L1
        action  -name *.exe
        braf    L2
L1      action  -print
L2:     halt

無駄な
not
の削除

$ findc ! ! -executable
// path: .
        action  -executable
        not
        not
        braf    L1
        action  -print
L1:     halt

連続する二つの

not
は相殺されるため、指令を削除できます。

ピープルオプティマイザ

コンパイラは以下のような小さな改善を繰り返し行うことで最適化します:

  • not-not
    を削除
  • brat
    braf
    (または逆)の再ターゲット
  • 同一ジャンプ先へのジャンプはそのターゲットへ直接移動
  • not-braf
    brat
    に、
    not-brat
    braf
    に変換
  • halt
    前の副作用を持たない命令(例:
    not-halt
    )を削除
  • 常に真となるアクション(例:
    -print-braf
    )はブランチを省略

実装には、分岐先を検出して修正できるよう少しだけ豊富な内部表現が必要です。

さらに複雑なケース

$ findc -type f ! \( -executable -o -name '*.exe' \)
// path: .
        action  -type f
        braf    L1
        action  -executable
        brat    L2
        action  -name *.exe
L2:     not
L1:     braf    L3
        action  -print
L3:     halt

最適化されていないジャンプが見えるのは、コンパイラ構造上の欠点を示しています。
挑戦したい方はここで止まり、どのようにコンパイラを設計すればこうしたアーティファクトを生成しないか考えてみてください。


パーシングとコンパイル

バイトコードの形を決める前に、

find
の中置表記(infix)を コンパイラが扱いやすい後置表記(postfix)へ変換する必要があります。
例:

-type f -a ! ( -executable -o -name *.exe )

-type f -executable -name *.exe -o ! -a

となり、括弧が消去されます。
入力は

argv
配列として受け取るため、すでにシェルやランタイムによってトークン化されています。

ショーティングヤードアルゴリズム

  • アクション はそのまま出力キューへ
  • 特殊スタックトークン(
    -a
    ,
    -o
    ,
    !
    ,
    (
    )を見たら、 より高い優先順位の演算子をスタックからポップしてキューへ追加し、
    (
    に到達するまで続ける
  • )
    を見たら、
    (
    が出てくるまでスタックからポップしてキューへ

トークンがなくなったら残りのスタックをすべてキューへ。
パーサは暗黙的に

-a
(論理 AND)を挿入し、
-exec
,
-ok
,
-print
がない場合は解析完了後に
-print
-a
をキューに追加して
( expr ) -print
で包みます。

バイトコードへの変換

コンパイラは「バイトコードフラグメント」のスタックを維持します。
各フラグメントは一連のバイトコード命令です。
ジャンプは相対アドレスを使用するため位置に依存せず、 フラグメントを結合してもブランチ先は変更しません。

トークンごとの処理:

  1. アクション
    action
    命令を作成し、新しいフラグメントとしてスタックへプッシュ
  2. !
    – スタックのトップフラグメントを取り出し、末尾に
    not
    を追加して再度プッシュ
  3. -a
    – スタックから上位 2 フラグメントをポップし、真偽が偽の場合に次のフラグメントへジャンプする
    braf
    を挿入して結合。
    (左辺が偽なら右辺をスキップ)
  4. -o
    – 上記と同様だが、左辺が真の場合にジャンプする
    brat

式が正当であれば、処理後のスタックには 1 つのフラグメントだけ残ります。
その末尾に

halt
を追加し、これが最終的なプログラムです。
もし最後のフラグメントにブランチがある場合は、そのブランチ先として
halt
が位置します。

ピープルオプティマイザを加えることで、この命令セットで最適なプログラムを生成できると考えられます。

同じ日のほかのニュース

一覧に戻る →

2025/12/27 2:13

**原文:** 「How uv got so fast」 **改訂(整理済み):** 「How did UV get so fast?」 **日本語訳:** 「UVはなぜそんなに速くなるのですか?」

## Japanese Translation: ``` ## Summary uv は Rust のみを利用するよりも、モダンな設計選択とパッケージング標準を採用しているため、pip より優れています。不要なオーバーヘッドを伴うレガシー機能(.egg サポート、pip.conf の読み込み、デフォルトのバイトコードコンパイル、system‑Python インストールの許可、厳格な仕様準拠、requires‑python の上限無視、複数インデックスが設定されている場合に最初のインデックスを選択)を削除することで、uv は不要なコードパスを排除します。さらに、HTTP レンジリクエストによる部分的な wheel ダウンロード、並列ダウンロード、ハードリンクまたはコピーオンライト可能なグローバルキャッシュ、ネイティブ TOML パース、および効率的な PubGrub 依存関係解決器を使用してインストール速度を加速させています。Rust は rkyv によるゼロコピー逆シリアライズ、ロックフリーの同時実行データ構造、単一静的バイナリであるためのインタプリタ起動コストゼロ、および高速比較/ハッシュのためのコンパクトな 64‑ビットバージョン表現を提供します。 Python のパッケージングは、setup.py スクリプトがインストール時に実行される必要があったため遅くていました。PEP 518–658 の導入によりメタデータが宣言的フォーマットへ移行し、PyPI 上の Simple API(2023年5月以降利用可能)が有効になりました。uv は 2024 年2月にリリースされ、初日からこれら新しい標準を活用しています。 影響は大きく、開発者は依存関係をより迅速にインストールでき、軽量な環境を構築できます。これは、Python エコシステム全体で最新のパッケージング標準を完全にサポートするツールへの広範な移行を促進する可能性があります。 ```

2025/12/23 23:51

2025年の最高アイテムとおすすめ

## Japanese Translation: ## Summary 2025年、著者はハンス・ボーマーの再帰的実数計算法や日本漫画カフェドキュメンタリー『マンブー』などの注目すべきIT・テック発見で生産性の高い一年を達成しました。彼はClojureのcore.asyncとJDK 21バーチャルスレッド、結合型関数言語Juxt、WordPressからMarkdown/org-mode静的サイトパイプラインへの移行についての技術ブログを複数公開しました。また、ブラジルで開催されたClojure/conjとClojure South Brazilという二つの主要なClojureカンファレンスに参加し、コミュニティの影響力を強調しました。 色分けされた月次マトリクスを備えたスプレッドシートベースのタスクトラッカーが導入され、著者のミニマリストでシステム思考的アプローチを反映し、専用プランナーよりもスプレッドシートを好む姿勢を示しました。プログラミングはClojure(16年にわたるフルタイム作業)に集中しつつ、Joy、Clojerl、Scittle、Javaによるコンパイラ開発も探索しました。 2025年にはゲーム・フィクション・システム思考に関するエッセイなど非技術的なアウトプットが増加し、2026年にはさらに多くのフィクションとカードゲームルールを企画しています。著者の「人生を変えるテクノロジー」主張はZettelkastenメモ取り法の採用に集約され、新しい非技術的投稿が可能になりました。大規模言語モデル(LLM)との広範な実験は限定的な実践的利益しか生まれず、問題設定とソクラテス対話における欠点を批判しています。 また、本年には好きな本(例:『オシリスの目』、『ナルキッサスとゴールドマン』)、音楽、映画、ポッドキャスト、テーブルトップゲームの厳選リストも含まれました。2026年を見据えて、著者は非技術的執筆を増やし、カードゲームルールを発表し、Clojure 1.13を進化させ、物理アーティファクトを作成し、未翻訳の本を読み、Goodnotes、Antinet、Booxなどのツールを使ってテックラダーを洗練する予定です。すべてはミニマリストでシステム志向のワークフローを維持しながら行われます。 この要約は元のリストからすべての主要ポイントを取り込み、推測を避け、明確な主旨を提示し、曖昧または混乱する表現を排除しています。

2025/12/27 8:09

「テキストに常に賭ける」

## Japanese Translation: ## Summary テキストは、これまでに発明された中で最も強力で多用途かつ信頼性の高い通信技術であり、可能な限り常に選択すべきです。約5,000年間安定した媒体として存在し、その長寿を示す耐久的な遺物が残っています。テキストは比類のない柔軟性を提供します:正確な意味を伝えることができる一方で、暗黙の文脈も許容するため、文学・歴史・哲学・数学・論理学・プログラミング・工学・注釈学・ファンフィクションに不可欠です。他のメディアと比べてテキストは格段に効率的であり(例:5 kB のブログ投稿がより大きな画像と同じ情報量を持つ)、電信・電子メール・チャット・ウェブなど後続技術を可能にしました。テキストは一対一、一対多、多対多のあらゆる社会的相互作用をサポートし、検索性・索引化・翻訳・非同期性・アルゴリズム処理(例:差分検出や要約)が可能です。また、多者編集、分岐会話、注釈付け、引用、構造化応答、およびレビューも許容します。著者は、テキストが通信・データ保存・アルゴリズム処理の基盤メディアとして残ると信じており、その幅広さ・深さ・スケーラビリティに匹敵するフォーマットは他に存在しないと述べています

**Unix `find`式のバイトコード化** * **目的** 複雑な検索条件を、`find` ユーティリティが効率的に評価できるコンパクトで実行可能な形に変換すること。 * **主な構成要素** - **演算子**:論理(`-and`、`-or`、`!`)、比較(`-eq`、`-ne`、`-lt`、`-gt` 等) - **オペランド**:ファイル属性(名前・サイズ・種類)、タイムスタンプ、パーミッション、所有者、内容チェックなど * **コンパイル手順** 1. ユーザーが入力した式を抽象構文木(AST)に解析する。 2. AST をバイトコード命令列へ変換する。 3. 定数の折りたたみや不要な分岐の簡略化で最適化する。 * **バイトコード形式** - 各命令は固定長レコード:opcode + オペランド。 - opcode は `CHECK_NAME`、`COMPARE_SIZE`、`JUMP_IF_FALSE` 等を表す。 - オペランドは即値、文字列リテラル、または制御フロー用オフセットになる。 * **実行** `find` エンジンは検索ツリー内の各ファイルに対してバイトコードを解釈し、小さな仮想マシンでブール結果スタックを更新する。 * **メリット** - 各ファイルごとに式を再解析するより高速。 - 完全な AST をメモリに保持するよりも記憶域が節約できる。 * **典型的な使用例** ```bash find /path -type f -size +1M -exec gzip {} \; ``` `-type f -size +1M` の式は実行前にバイトコードへコンパイルされる。 | そっか~ニュース