<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja-JP"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/atom.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" hreflang="ja-JP" /><updated>2026-10-05T19:08:54+09:00</updated><id>/atom.xml</id><title type="html">tanapp</title><subtitle>目指せ、個人開発</subtitle><author><name>Tana Gone</name></author><entry><title type="html">Neovim Luaスクリプト解説</title><link href="/swiftui/neovim-lua/" rel="alternate" type="text/html" title="Neovim Luaスクリプト解説" /><published>2026-10-05T18:56:36+09:00</published><updated>2026-10-05T18:56:36+09:00</updated><id>/swiftui/neovim-lua</id><content type="html" xml:base="/swiftui/neovim-lua/"><![CDATA[<p>neovimのluaと仲良くなるためにluaスクリプトを実行してみた。</p>

<ul>
  <li>
    <p>次のコマンドでソース・ファイルの実行ができる</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:source %
:luafile %
:messages
:lua vim.cmd('messages clear')
</code></pre></div>    </div>

    <p>source code</p>

    <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- a.lua</span>
<span class="n">vim</span><span class="p">.</span><span class="n">opt</span><span class="p">.</span><span class="n">helplang</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"ja"</span><span class="p">,</span> <span class="s2">"en"</span> <span class="p">}</span>
<span class="nb">print</span><span class="p">(</span><span class="n">vim</span><span class="p">.</span><span class="n">opt</span><span class="p">)</span>
<span class="n">vim</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">vim</span><span class="p">.</span><span class="n">opt</span><span class="p">)</span>
<span class="n">vim</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">vim</span><span class="p">.</span><span class="n">opt</span><span class="p">.</span><span class="n">helplang</span><span class="p">:</span><span class="n">get</span><span class="p">())</span>
</code></pre></div>    </div>

    <p>Output(luafile %; messages)</p>

    <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"a.lua"</span> <span class="mi">2</span><span class="n">L</span><span class="p">,</span> <span class="mi">49</span><span class="n">B</span>
<span class="n">table</span><span class="p">:</span> <span class="mh">0x0100c2ee48</span>
<span class="p">{</span>
  <span class="o">&lt;</span><span class="n">metatable</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">__index</span> <span class="o">=</span> <span class="o">&lt;</span><span class="k">function</span> <span class="err">1&gt;,</span>
    <span class="nf">__newindex</span> <span class="o">=</span> <span class="o">&lt;</span><span class="k">function</span> <span class="err">2&gt;</span>
  <span class="err">}</span>
<span class="err">}</span>
<span class="err">{</span> <span class="err">"</span><span class="nf">ja</span><span class="s2">", "</span><span class="n">en</span><span class="s2">" }
</span></code></pre></div>    </div>
  </li>
  <li>
    <p>vimオブジェクトを含めてすべてのオブジェクトを列挙する</p>

    <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- グローバル空間(_G)にあるものをすべて列挙する</span>
<span class="kd">local</span> <span class="n">keys</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span> <span class="nb">pairs</span><span class="p">(</span><span class="n">_G</span><span class="p">)</span> <span class="k">do</span>
	<span class="nb">table.insert</span><span class="p">(</span><span class="n">keys</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="k">end</span>
<span class="nb">table.sort</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span>
  
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">name</span> <span class="k">in</span> <span class="nb">ipairs</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span> <span class="k">do</span>
	<span class="kd">local</span> <span class="n">value_type</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">_G</span><span class="p">[</span><span class="n">name</span><span class="p">])</span>
	<span class="nb">print</span><span class="p">(</span><span class="nb">string.format</span><span class="p">(</span><span class="s2">"Global: %-15s [%s]"</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value_type</span><span class="p">))</span>
<span class="k">end</span>
</code></pre></div>    </div>

    <p>output</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Global: LazyVim         <span class="o">[</span>table]
Global: MiniAi          <span class="o">[</span>table]
Global: MiniIcons       <span class="o">[</span>table]
Global: MiniPairs       <span class="o">[</span>table]
Global: Snacks          <span class="o">[</span>table]
Global: _               <span class="o">[</span>number]
Global: _G              <span class="o">[</span>table]
Global: _PlenaryLeafTable <span class="o">[</span>table]
Global: _VERSION        <span class="o">[</span>string]
Global: ___bufferline_private <span class="o">[</span>table]
Global: ___neotree_selector_click <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: arg             <span class="o">[</span>table]
Global: assert          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: bit             <span class="o">[</span>table]
Global: collectgarbage  <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: coroutine       <span class="o">[</span>table]
Global: debug           <span class="o">[</span>table]
Global: dofile          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: error           <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: gcinfo          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: getfenv         <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: getmetatable    <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: io              <span class="o">[</span>table]
Global: ipairs          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: jit             <span class="o">[</span>table]
Global: load            <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: loadfile        <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: loadstring      <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: lpeg            <span class="o">[</span>table]
Global: math            <span class="o">[</span>table]
Global: module          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: newproxy        <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: next            <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: noice_C         <span class="o">[</span>userdata]
Global: nvim_bufferline <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: os              <span class="o">[</span>table]
Global: package         <span class="o">[</span>table]
Global: pairs           <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: pcall           <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: print           <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: rawequal        <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: rawget          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: rawset          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: require         <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: <span class="k">select</span>          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: setfenv         <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: setmetatable    <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: string          <span class="o">[</span>table]
Global: svim            <span class="o">[</span>table]
Global: table           <span class="o">[</span>table]
Global: tonumber        <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: tostring        <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: <span class="nb">type</span>            <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: unpack          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
Global: vim             <span class="o">[</span>table]
Global: xpcall          <span class="o">[</span><span class="k">function</span><span class="o">]</span>
</code></pre></div>    </div>

    <p>Lua言語が標準で提供しているオブジェクト群です。vim を介さずに直接呼び出せます。
• string : 文字列操作（string.format や string.gsub など）
• table : テーブル操作（table.insert や table.sort など）
• math : 数学計算（math.sin や math.random など）
• io : 外部ファイルの入出力操作
• os : 時間の取得や、OSコマンドの実行（os.time、os.execute）
• package : モジュールの検索パス（先ほど使った package.path）の管理</p>

    <p>Neovimには、非同期処理や高速な構文解析のために最初から以下の強力なライブラリがグローバル展開されています。</p>

    <ul>
      <li>
        <p><strong><code class="language-plaintext highlighter-rouge">uv</code> (または <code class="language-plaintext highlighter-rouge">vim.loop</code>)</strong></p>

        <ul>
          <li>Neovimの非同期処理やイベントループを支える <strong>Libuv</strong>（Node.jsでも使われているライブラリ）のオブジェクトです。</li>
          <li>ファイルの非同期読み込み、タイマー処理（〇秒後に実行）、外部プロセスの監視などを実行できます。</li>
        </ul>
      </li>
      <li>
        <p><strong><code class="language-plaintext highlighter-rouge">jit</code></strong></p>

        <ul>
          <li>Neovimが採用している超高速なLua実行エンジン <strong>LuaJIT</strong>（Just-In-Timeコンパイラ）を制御・確認するためのオブジェクトです。</li>
          <li><code class="language-plaintext highlighter-rouge">jit.version</code> で現在のJITバージョンを確認したり、最適化の状態を取得できます。</li>
        </ul>
      </li>
    </ul>

    <p>特殊なシステム変数・環境
• _G (グローバルテーブル)
• Luaのすべてのグローバル変数が格納されている大元のテーブルです。
• vim や string、あなたが自作したローカルでない変数も、すべて実体はこの _G というオブジェクトの中にあります（例：_G.vim と vim は全く同じものを指します）。
• _VERSION
• 現在動作しているLuaのバージョン（Neovimであれば基本 “Lua 5.1”）を返す文字列オブジェクトです。</p>
  </li>
  <li>
    <p>vim.optに含まれるすべてのオブジェクトを列挙する</p>

    <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- 1. Neovimが持つすべてのオプション情報を取得する</span>
<span class="kd">local</span> <span class="n">all_options</span> <span class="o">=</span> <span class="n">vim</span><span class="p">.</span><span class="n">api</span><span class="p">.</span><span class="n">nvim_get_all_options_info</span><span class="p">()</span>
  
<span class="c1">-- 2. アルファベット順に並び替えるためにキー（名前）だけを抽出してソート</span>
<span class="kd">local</span> <span class="n">keys</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span> <span class="nb">pairs</span><span class="p">(</span><span class="n">all_options</span><span class="p">)</span> <span class="k">do</span>
    <span class="nb">table.insert</span><span class="p">(</span><span class="n">keys</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="k">end</span>
<span class="nb">table.sort</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span>
  
<span class="c1">-- 3. ソートした順に vim.opt からオブジェクトを取得して出力</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">name</span> <span class="k">in</span> <span class="nb">ipairs</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span> <span class="k">do</span>
    <span class="c1">-- vim.opt.[設定名] の形でオブジェクトにアクセスする</span>
    <span class="kd">local</span> <span class="n">opt_object</span> <span class="o">=</span> <span class="n">vim</span><span class="p">.</span><span class="n">opt</span><span class="p">[</span><span class="n">name</span><span class="p">]</span>
      
    <span class="c1">-- 現在の設定値を安全に取得する（:get() メソッドを使用）</span>
    <span class="kd">local</span> <span class="n">success</span><span class="p">,</span> <span class="n">current_value</span> <span class="o">=</span> <span class="nb">pcall</span><span class="p">(</span><span class="k">function</span><span class="p">()</span> <span class="k">return</span> <span class="n">opt_object</span><span class="p">:</span><span class="n">get</span><span class="p">()</span> <span class="k">end</span><span class="p">)</span>
      
    <span class="k">if</span> <span class="n">success</span> <span class="k">then</span>
        <span class="c1">-- 1行で綺麗に出力する</span>
        <span class="nb">print</span><span class="p">(</span><span class="nb">string.format</span><span class="p">(</span><span class="s2">"vim.opt.%-15s = %s"</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">vim</span><span class="p">.</span><span class="n">inspect</span><span class="p">(</span><span class="n">current_value</span><span class="p">)))</span>
    <span class="k">end</span>
<span class="k">end</span>
</code></pre></div>    </div>

    <p>コードのポイント
• vim.api.nvim_get_all_options_info(): Neovimが内部で持っている number や shiftwidth などの全オプションの名前とメタ情報をテーブルで返してくれます。
• vim.opt[name]: 文字列を使って動的にオブジェクトへアクセスしています。（vim.opt.number と vim.opt[“number”] は同じ意味です）
• opt_object:get(): vim.opt.xxx は特殊なオブジェクト（メタテーブル）であるため、その設定値（true や数値、テーブルなど）を取り出すには :get() メソッドを呼ぶ必要があります。
• pcall(): 万が一、特定の環境で取得できないオプションがあってもエラーで処理が止まらないように安全弁をかけています。
実行すると、数百行に及ぶNeovimのすべての設定項目とその現在の値（true / false / 数値 / リスト等）がコンソールにずらりと表示されます。</p>
  </li>
  <li>
    <p>vimに含まれるすべてのオブジェクトを列挙する方法グローバルな <strong><code class="language-plaintext highlighter-rouge">vim</code> テーブル</strong>（<code class="language-plaintext highlighter-rouge">vim.api</code> や <code class="language-plaintext highlighter-rouge">vim.fn</code> などの親玉）に含まれる直下のモジュールやオブジェクト、関数をすべて列挙するには、通常のテーブルと同様に <strong><code class="language-plaintext highlighter-rouge">pairs(vim)</code></strong> を使ってループ処理ができます。</p>

    <p>Neovimの <code class="language-plaintext highlighter-rouge">vim</code> テーブルの直下は通常のLuaテーブル構造になっているため、<code class="language-plaintext highlighter-rouge">vim.opt</code> のような特殊な工夫をせずとも簡単に列挙できます。</p>
  </li>
</ul>

<hr />

<p>すべてのオブジェクト（キーと型）を列挙するコード</p>

<p>以下のコードをファイルに貼り付けて <code class="language-plaintext highlighter-rouge">source %</code> で実行してください。</p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">-- 1. アルファベット順に並び替えるためにキー（名前）だけを抽出</span>
  <span class="kd">local</span> <span class="n">keys</span> <span class="o">=</span> <span class="p">{}</span>
  <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span> <span class="nb">pairs</span><span class="p">(</span><span class="n">vim</span><span class="p">)</span> <span class="k">do</span>
      <span class="nb">table.insert</span><span class="p">(</span><span class="n">keys</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
  <span class="k">end</span>
  <span class="nb">table.sort</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span>
  
  <span class="c1">-- 2. ソートした順にオブジェクトの名前と「型（Type）」を出力</span>
  <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">name</span> <span class="k">in</span> <span class="nb">ipairs</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span> <span class="k">do</span>
      <span class="kd">local</span> <span class="n">value</span> <span class="o">=</span> <span class="n">vim</span><span class="p">[</span><span class="n">name</span><span class="p">]</span>
      <span class="kd">local</span> <span class="n">value_type</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="c1">-- table, function, string などの型を取得</span>
      
      <span class="c1">-- 1行で綺麗に出力する</span>
      <span class="nb">print</span><span class="p">(</span><span class="nb">string.format</span><span class="p">(</span><span class="s2">"vim.%-15s [%s]"</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value_type</span><span class="p">))</span>
  <span class="k">end</span>
</code></pre></div></div>

<pre><code class="language-output">  vim.api           [table]     &lt;-- Neovim独自の強力なAPI群
  vim.cmd           [function]  &lt;-- Vimコマンドを実行する関数
  vim.env           [table]     &lt;-- 環境変数にアクセスするオブジェクト
  vim.fn            [table]     &lt;-- 従来のVimScript組み込み関数群
  vim.g             [table]     &lt;-- グローバル変数 (g:)
  vim.inspect       [function]  &lt;-- テーブルの中身を文字列化する関数
  vim.keymap        [table]     &lt;-- キーマッピングを設定するオブジェクト
  vim.lsp           [table]     &lt;-- 内蔵LSP（言語サーバー）関連
  vim.opt           [table]     &lt;-- オプション設定オブジェクト
  ...
</code></pre>

<p>さらに深掘り：中身を完全にのぞき見したい場合</p>

<p>もし「名前の列挙」だけでなく、<code class="language-plaintext highlighter-rouge">vim</code> テーブルの全構造を丸ごと見たい場合は、Neovimに内蔵されている <strong><code class="language-plaintext highlighter-rouge">vim.inspect()</code></strong> を使うと一発でダンプできます。 [<a href="https://github.com/willelz/nvim-lua-guide-ja">1</a>, <a href="https://github.com/aikawa9376/nvim-lua-guide-ja">2</a>]</p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">-- vimテーブルのすべての階層・中身を完全にシリアライズして出力</span>
  <span class="c1">-- (非常に長大なテキストになるので注意してください)</span>
  <span class="nb">print</span><span class="p">(</span><span class="n">vim</span><span class="p">.</span><span class="n">inspect</span><span class="p">(</span><span class="n">vim</span><span class="p">))</span>
</code></pre></div></div>

<p>特定のサブモジュール（例：<code class="language-plaintext highlighter-rouge">vim.api</code> の中にある全関数の一覧など）だけを調べたい場合は、上記の列挙コードの <code class="language-plaintext highlighter-rouge">pairs(vim)</code> の部分を <code class="language-plaintext highlighter-rouge">pairs(vim.api)</code> に書き換えることで同様に確認できます。</p>

<ul>
  <li>
    <p>luaのPlayerオブジェクトを呼出す。ファイルの相対パス(%)から絶対パス(%:p)を取出し、さらにヘッダー部を除去(%:p:h)しオブジェクトが記述されたplayer.luaファイルが存在するパスをpackage.path変数へセットする。そうすれば、requireの引数にファイル名を記述してnewメソッドを呼び出せる。</p>

    <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- 現在開いているファイルのディレクトリを検索パスに追加する</span>
<span class="kd">local</span> <span class="n">current_dir</span> <span class="o">=</span> <span class="n">vim</span><span class="p">.</span><span class="n">fn</span><span class="p">.</span><span class="n">expand</span><span class="p">(</span><span class="s2">"%:p:h"</span><span class="p">)</span>
<span class="nb">package.path</span> <span class="o">=</span> <span class="n">current_dir</span> <span class="o">..</span> <span class="s2">"/?.lua;"</span> <span class="o">..</span> <span class="nb">package.path</span>
  
<span class="c1">-- その後で require する</span>
<span class="kd">local</span> <span class="n">foo</span> <span class="o">=</span> <span class="nb">require</span><span class="p">(</span><span class="s2">"player"</span><span class="p">).</span><span class="n">new</span><span class="p">(</span><span class="s2">"Taro"</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
<span class="n">vim</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="s2">"foo.hp = "</span> <span class="o">..</span> <span class="n">foo</span><span class="p">.</span><span class="n">hp</span><span class="p">)</span> <span class="c1">-- テーブルのアドレス（あるいは__tostringの結果）が表示されます</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"foo = "</span><span class="p">,</span> <span class="n">foo</span><span class="p">)</span> <span class="c1">-- テーブルのアドレス（あるいは__tostringの結果）が表示されます</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>luaのオブジェクト</p>

    <div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- 1. クラス（設計図）となるテーブルを定義する</span>
<span class="kd">local</span> <span class="n">Player</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">Player</span><span class="p">.</span><span class="n">__index</span> <span class="o">=</span> <span class="n">Player</span> <span class="c1">-- 自分自身をインデックスに指定</span>
  
<span class="c1">-- 2. コンストラクタ（インスタンス作成関数）を定義する</span>
<span class="k">function</span> <span class="nc">Player</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">hp</span><span class="p">)</span> <span class="c1">-- コンストラクタの名前は任意 e.g. setup</span>
	<span class="kd">local</span> <span class="n">self</span> <span class="o">=</span> <span class="nb">setmetatable</span><span class="p">({},</span> <span class="n">Player</span><span class="p">)</span> <span class="c1">-- 空のテーブルにメタテーブルを設定</span>
	<span class="n">self</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
	<span class="n">self</span><span class="p">.</span><span class="n">hp</span> <span class="o">=</span> <span class="n">hp</span>
	<span class="k">return</span> <span class="n">self</span>
<span class="k">end</span>
  
<span class="c1">-- 3. メソッド（メンバ関数）を追加する</span>
<span class="k">function</span> <span class="nf">Player</span><span class="p">:</span><span class="n">attack</span><span class="p">()</span> <span class="c1">-- .attack(self)でもOK</span>
	<span class="nb">print</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">name</span> <span class="o">..</span> <span class="s2">" の攻撃！"</span><span class="p">)</span>
<span class="k">end</span>
  
<span class="c1">-- ここを追加！print() されたときの表示形式を定義する</span>
<span class="k">function</span> <span class="nf">Player</span><span class="p">:</span><span class="n">__tostring</span><span class="p">()</span>
	<span class="k">return</span> <span class="nb">string.format</span><span class="p">(</span><span class="s2">"Player(Name: %s, HP: %d)"</span><span class="p">,</span> <span class="n">self</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="n">self</span><span class="p">.</span><span class="n">hp</span><span class="p">)</span>
<span class="k">end</span>
  
<span class="c1">-- 4. オブジェクト（インスタンス）を作成して使ってみる</span>
<span class="c1">-- local player1 = Player.new("勇者", 100)</span>
<span class="c1">-- player1:attack() -- 出力: 勇者 の攻撃！</span>
<span class="k">return</span> <span class="n">Player</span>
</code></pre></div>    </div>

    <p>ポイント</p>

    <ul>
      <li><strong><code class="language-plaintext highlighter-rouge">setmetatable(table, metatable)</code></strong>: テーブルにメタテーブルを設定し、独自の振る舞いやプロパティの継承を持たせます。</li>
      <li><strong><code class="language-plaintext highlighter-rouge">__index</code></strong>: テーブルに存在しないキーにアクセスされたとき、参照する別のテーブルを指定します。これにより、クラス側で定義したメソッドを各インスタンスから共有できます。</li>
      <li><strong>コロン構文（<code class="language-plaintext highlighter-rouge">obj:method()</code>）</strong>: 第一引数に自動的に <code class="language-plaintext highlighter-rouge">self</code>（インスタンス自身）を渡すためのLuaの糖衣構文（ショートカット）です。 [<a href="https://qiita.com/metaphysical_bard/items/2733ed130d5037da8dd6">1</a>, <a href="https://www.ibm.com/docs/ja/netezza?topic=lpl-metatables">2</a>, <a href="https://qiita.com/demoin/items/fe2d243fa79745977758">3</a>, <a href="https://light11.hatenadiary.com/entry/2021/08/05/203929">4</a>, <a href="https://www.nct9.ne.jp/m_hiroi/light/lua05a.html">5</a>]</li>
    </ul>
  </li>
  <li>
    <p>SwiftからLuaコードを実行データの受け渡しを行うには、<strong>LuaのC API</strong>（またはそれをラップしたサードパーティ製ライブラリである「SwiftLua」など）を利用します。</p>

    <p>ここでは、最も標準的で外部ライブラリへの依存が少ない、LuaのCライブラリを直接Swiftから呼び出す方法（<strong>LuaC / Lua 5.1 API</strong>）を想定したコードをご案内します。Neovimが内蔵しているLuaJIT（Lua 5.1互換）とも非常に親和性が高い書き方です。</p>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p>配列arを受取り配列brを返すluaのコードを実行するSwiftのコード</p>

    <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">Foundation</span><span class="c1">// ※LuaのCヘッダー（lua.hなど）をインポート可能な状態にしている前提です// （Swiftプロジェクトのブリッジングヘッダーなどで import しておきます）let ar: [Float] = [0.0, 1.0, 2.0]var br: [Float] = []// 1. Luaステート（仮想マシン環境）を作成let L = luaL_newstate()luaL_openlibs(L)// --- SwiftからLuaへのデータ転送 (ar配列をLuaのテーブルにする) ---lua_createtable(L, Int32(ar.count), 0) // 空のテーブルをスタックに積むfor (index, value) in ar.enumerated() {    lua_pushnumber(L, Double(value))    // Luaの配列は1始まりなので index + 1    lua_rawseti(L, -2, Int32(index + 1))}// グローバル変数 "ar" としてLua環境に登録lua_setglobal(L, "ar")// 2. Luaコードの定義と実行（仕様2: 各要素を2倍にする処理）let luaScript = """br = {}for i = 1, #ar do    br[i] = ar[i] * 2end"""// Luaコードを読み込んで実行if luaL_dostring(L, luaScript) != 0 {    let errorMsg = String(cString: lua_tostring(L, -1))    print("Luaエラー: \(errorMsg)")} else {    // 3. Luaコードが生成した配列（テーブル "br"）をSwiftのbr配列へ代入    lua_getglobal(L, "br") // グローバル変数 "br" をスタックに取得        if lua_istable(L, -1) {        // Luaのテーブル（配列）の長さを取得        let len = lua_objlen(L, -1)                 for i in 1...len {            lua_rawgeti(L, -1, Int32(i)) // br[i] を取得してスタックのトップに積む            let value = lua_tonumber(L, -1)            br.append(Float(value))            lua_pop(L, 1) // 読み終わった値をスタックからポップ        }    }}// 4. Luaステートのクローズ（メモリ解放）lua_close(L)// 結果の確認print("ar: \(ar)") // [0.0, 1.0, 2.0]print("br: \(br)") // [0.0, 2.0, 4.0]</span>
</code></pre></div>    </div>

    <p>コードの解説</p>

    <ol>
      <li><strong><code class="language-plaintext highlighter-rouge">luaL_newstate()</code></strong>
Swiftの中で独立した小さなLua実行環境（ステート）を立ち上げます。</li>
      <li><strong><code class="language-plaintext highlighter-rouge">lua_createtable</code> と <code class="language-plaintext highlighter-rouge">lua_rawseti</code></strong>
Luaの内部は「スタック」と呼ばれる仕組みでデータをやり取りします。Swiftの配列 <code class="language-plaintext highlighter-rouge">[0.0, 1.0, 2.0]</code> を1つずつLuaの数値に変換しながら、Lua側の配列（テーブル）の中に <code class="language-plaintext highlighter-rouge">ar[1] = 0.0</code>, <code class="language-plaintext highlighter-rouge">ar[2] = 1.0</code> のようにセットしています。</li>
      <li><strong><code class="language-plaintext highlighter-rouge">luaL_dostring(L, luaScript)</code></strong>
指定したLuaコードの文字列をその場でコンパイルして実行します。Lua側ではグローバルに配置された <code class="language-plaintext highlighter-rouge">ar</code> テーブルを読み込み、要素数を表す <code class="language-plaintext highlighter-rouge">#ar</code> を使ってループを回し、2倍にした新しいテーブル <code class="language-plaintext highlighter-rouge">br</code> を作成しています。</li>
      <li><strong><code class="language-plaintext highlighter-rouge">lua_objlen</code> と <code class="language-plaintext highlighter-rouge">lua_tonumber</code></strong>
Lua側で作られた <code class="language-plaintext highlighter-rouge">br</code> テーブルの要素数を調べ、1番目から順にSwiftの <code class="language-plaintext highlighter-rouge">Float</code> 型へキャストしながら、<code class="language-plaintext highlighter-rouge">br.append()</code> でSwift側の配列へ戻しています。</li>
    </ol>
  </li>
</ul>

<hr />

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>この実装を実際に動かすには、Swiftのビルド環境（XcodeやSwift Package Manager）に **Luaのスタティックライブラリ（`liblua.a`）やヘッダーファイル** をリンクさせる必要があります。
</code></pre></div></div>

<ul>
  <li>Swift向けのLuaラッパーライブラリ</li>
</ul>

<ol>
  <li>主なGitHubリポジトリ（オープンソース・ライブラリ）</li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- ***\*[SwiftyLua (GitHub)](https://github.com/SwiftyLua/SwiftyLua)\****

  - SwiftからLuaを扱うためのブリッジ・ラッパーライブラリです。`Package.swift` への依存関係の追加方法や、テストコード内での具体的なユースケースのサンプルが用意されています。 [[1](https://github.com/SwiftyLua/SwiftyLua), [2](https://github.com/SwiftyLua/SwiftyLua)]

  

- ***\*[LuaSwift (GitHub)](https://github.com/tomsci/LuaSwift)\****

  - Lua 5.4のC APIを薄くラップした、外部依存関係のないシンプルなSwiftパッケージです。メタテーブルのブリッジングや、エラーをSwiftの `throw` に変換する機能など、実用的な連携機能が備わっています。 [[1](https://github.com/tomsci/LuaSwift), [2](https://github.com/tomsci/LuaSwift)]

  

- ***\*[LuaSwift - Lightweight Wrapper (GitHub)](https://github.com/ChrisGVE/LuaSwift)\****

  - iOSやmacOSアプリケーションにLuaを埋め込むためのモダンなSwiftインターフェースです。App Storeのガイドラインに準拠できるよう、LuaのCソースコードが直接バンドルされています。
  - このライブラリの詳細なドキュメントは、パッケージ管理サイトの ***\*[LuaSwift Documentation (Swift Package Index)](https://swiftpackageindex.com/ChrisGVE/LuaSwift/v1.12.4/documentation/luaswift)\**** からも確認できます。 [[1](https://github.com/ChrisGVE/LuaSwift), [2](https://swiftpackageindex.com/ChrisGVE/LuaSwift/v1.12.4/documentation/luaswift)]
</code></pre></div></div>

<hr />

<ol>
  <li>日本語の解説記事・実装サンプル
• 【Swift】Lua言語環境をアプリに組み込む【Cygnus】 (Zenn)</li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[【Swift】Lua言語環境をアプリに組み込む【Cygnus】](https://zenn.dev/enchan1207/articles/ac580ec2734183)
• 日本の開発者（Enchan1207氏）による、Lua環境を丸ごとSwiftアプリケーションに組み込むための解説記事です。SwiftとLuaの親和性や、macOS向けアプリに実際にLuaのスクリプト環境を実装する流れがグラフィカルに解説されており、非常に参考になります。
• swift-lua-example (GitHubサンプル)
[shengdie/swift-lua-example: SwiftからLuaを呼び出すサンプル](https://github.com/shengdie/swift-lua-example)

• SwiftとC言語の仕組み（ブリッジングヘッダーなど）を使い、SwiftからLuaを呼び出す最小限の構成を示した日本語のサンプルプロジェクトです。ライブラリを挟まず、生に近い形で連携させる際の参考になります。
</code></pre></div></div>

<ul>
  <li>requireメソッドでPlayerオブジェクトを呼び出したように他の言語、例えばSwiftで作成されたStruct、Classを呼び出すSwiftで作成した <code class="language-plaintext highlighter-rouge">Struct</code> や <code class="language-plaintext highlighter-rouge">Class</code> をNeovim（Lua環境）から利用したい場合、主に<strong>2つのアプローチ</strong>があります。</li>
</ul>

<hr />

<p>アプローチ1：SwiftコードをC言語の形式（Cリンケージ）にしてライブラリ化する</p>

<p>LuaはC言語との親和性が非常に高いため、Swift側で<strong>「C言語のフリをした関数」</strong>を作り、それをLuaの <code class="language-plaintext highlighter-rouge">package.loadlib</code> などの機能を使って呼び出します。</p>

<ol>
  <li>Swift側のコード (<code class="language-plaintext highlighter-rouge">Player.swift</code>)</li>
</ol>

<p>Swiftのクラスや構造体を直接Luaに渡すことはできないため、C言語から呼べる関数（ラッパー）を経由させます。</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">// Swiftのクラスpublic class SwiftPlayer {    var name: String    var hp: Int        init(name: String, hp: Int) {        self.name = name        self.hp = hp    }}// C言語向けにエクスポートする関数@_cdecl("create_swift_player")public func createSwiftPlayer(name: UnsafePointer&lt;CChar&gt;, hp: Int32) -&gt; UnsafeMutableRawPointer {    let swiftName = String(cString: name)    let player = SwiftPlayer(name: swiftName, hp: Int(hp))        // Swiftオブジェクトの参照カウントを維持したままポインタとして返す    return UnsafeMutableRawPointer(Unmanaged.passRetained(player).toOpaque())}</span>
</code></pre></div></div>

<ol>
  <li>Lua（Neovim）側のコード</li>
</ol>

<p>このようにしてコンパイルした動的ライブラリ（例: <code class="language-plaintext highlighter-rouge">libswift_player.dylib</code>）を、Lua側から低レイヤーAPI（<code class="language-plaintext highlighter-rouge">package.loadlib</code>）や、<strong>LuaJITの <code class="language-plaintext highlighter-rouge">ffi</code> モジュール</strong>を使って呼び出します。特にNeovimに内蔵されている <strong><code class="language-plaintext highlighter-rouge">ffi</code></strong> を使うと、<code class="language-plaintext highlighter-rouge">require</code> のように手軽に外部ライブラリを扱えます。</p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">local</span> <span class="n">ffi</span> <span class="o">=</span> <span class="nb">require</span><span class="p">(</span><span class="s2">"ffi"</span><span class="p">)</span><span class="c1">-- C言語の型定義をLuaに教えるffi.cdef[[    void* create_swift_player(const char* name, int hp);]]-- Swiftでビルドした動的ライブラリを読み込むlocal swift_lib = ffi.load("./libswift_player.dylib")-- Swiftのオブジェクト（ポインタ）を作成local swift_player_ptr = swift_lib.create_swift_player("Taro", 100)print(swift_player_ptr) -- 外部オブジェクトのメモリアドレスが表示される</span>
</code></pre></div></div>

<p>アプローチ2：RPC（リモートプロシージャコール）通信を使う（Neovimでの現実的な方法）</p>

<p>もし目的が<strong>「SwiftでNeovimのプラグインや拡張機能を作りたい」</strong>ということであれば、ライブラリ化するよりも、Neovimの <strong>RPC（Msgpack-RPC）</strong> という仕組みを使うのが一般的かつ安全です。</p>

<p>Neovimは裏で外部プロセスと通信する強力な機能を備えています。</p>

<ol>
  <li>Swift側で独立したプログラム（実行ファイル）を作ります。</li>
  <li>NeovimのLua側から <code class="language-plaintext highlighter-rouge">vim.fn.jobstart({"./swift_program"})</code> を使ってそのプログラムを起動します。</li>
  <li>標準入出力（JSONやMessagePack形式）を介して、LuaとSwiftの間でデータのやり取りやオブジェクトの操作要求を行います。</li>
</ol>

<hr />

<p>まとめ</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">require("player")</code> のように<strong>完全にLuaオブジェクトと同じ感覚で直接インポートすることはできない</strong>。</li>
  <li>ただし、SwiftコードをC言語互換（<code class="language-plaintext highlighter-rouge">@_cdecl</code>）でビルドすれば、Neovimの <strong><code class="language-plaintext highlighter-rouge">ffi</code> モジュールを使ってLuaからSwiftの処理を呼び出せる</strong>。</li>
</ul>]]></content><author><name>Tana Gone</name></author><category term="SwiftUI" /><summary type="html"><![CDATA[neovimのluaと仲良くなるためにluaスクリプトを実行してみた。]]></summary></entry><entry><title type="html">日米業種リードラグ投資戦略</title><link href="/trading/nakagawa/" rel="alternate" type="text/html" title="日米業種リードラグ投資戦略" /><published>2026-08-27T08:17:37+09:00</published><updated>2026-08-27T08:17:37+09:00</updated><id>/trading/nakagawa</id><content type="html" xml:base="/trading/nakagawa/"><![CDATA[<p>投資戦略を紹介する動画で紹介された論文を論文解説WebサイトのAIに解説してもらった。</p>

<p><strong>部分空間正則化付き主成分分析を用いた日米業種リードラグ投資戦略</strong>
<br />
論文番号：SIG-FIN-036-13、掲載ページ：76–83</p>

<p><strong>Kei NAKAGAWA</strong>
<strong>Masahiro KATO</strong></p>

<p><strong>Graduate School of Business, Osaka Metropolitan University</strong></p>

<p><a href="https://chatgpt.com/g/g-hxDOCBQrs-paper-interpreter-japanese">GPTs Paper解説by紺野大地</a>
<br />
<a href="https://www.jstage.jst.go.jp/article/jsaisigtwo/2026/FIN-036/2026_76/_article/-char/ja/">原論文</a>
<br />
<a href="https://www.jstage.jst.go.jp/article/jsaisigtwo/2026/FIN-036/2026_76/_pdf/-char/ja">原論文pdf</a>
<br />
<a href="https://www.youtube.com/watch?v=Xvg4ZyzyZoc">日米市場のズレを突く投資戦略 橋本幸治の理系通信 3:00辺り</a>
<br />
<a href="https://kyoiku-kenkyudb.omu.ac.jp/html/100003047_ja.html">研究者詳細 - 中川　慧</a></p>

<p>t+1日目、日本側の標準化リターンの図示例</p>
<div align="center">
$$
\hat z_{J,t+1} = B_t^{(K)}z_{U,t} = (+0.066, -0.084, -0.005,...)
$$

<img src="/images/2026/rank_.png" alt="rank_" style="zoom:33%;" />
</div>
<p>注意: 1615東証のみ銀行業指数は除く
<a href="https://www.jpx.co.jp/equities/products/etfs/issues/01-03.html">日本株（業種別）銘柄一覧（ETF）</a></p>

<h1 id="abstract-"><strong>Abstract</strong> <a name="top"></a></h1>
<ul>
<a href="#background">Background</a>
<a href="#method">Methods</a>
<a href="#result">Result</a>
<a href="#discussion">Discussion</a>
</ul>
<p>本研究は、米国と日本の取引時間帯が重ならないことを利用し、<strong>米国市場で先に確定した業種別情報が、日本市場の翌営業日の寄付きから日中にかけて反映される</strong>というリード・ラグ仮説を検証したものである。</p>

<p>具体的には、米国の業種ETFについて当日の <strong>Close-to-Close return</strong> を情報として利用し、日本の業種ETFについて翌営業日の <strong>Open-to-Close return</strong> を予測する。</p>

<p>予測には、日米業種の結合相関行列に対して<strong>部分空間正則化付きPCA</strong>を適用する。通常のPCAでは短い推定期間における固有ベクトルの不安定性が問題となるため、グローバルファクター、国スプレッド、シクリカル・ディフェンシブという経済的に解釈可能な3次元の事前部分空間へ相関構造を縮約する。</p>

<p>その結果得られるシグナルは、米国業種リターンから日本業種リターンを予測する<strong>低ランク線形予測器</strong>として解釈できる。また、共通ファクターが米国で先に実現し、翌日に日本へ伝播する理想化モデルでは、この予測器が最良線形予測となることを理論的に示している。</p>

<p>実証分析では、提案手法の <strong>PCA SUB</strong> が、単純モメンタム、正則化なしPCA、モメンタムとのダブルソートを上回り、年率リターン <strong>23.79%</strong>、年率リスク <strong>10.70%</strong>、R/R <strong>2.22</strong>、最大ドローダウン <strong>9.58%</strong> を達成した。</p>

<h1 id="background-back"><strong>Background</strong> <a href="#top">back</a><a name="background"></a></h1>

<p>出発点は<strong>効率的市場仮説（EMH）</strong>である。EMHでは、利用可能な情報は資産価格へ迅速に反映されるため、リスク調整後の超過収益を継続的に得ることは難しいと考える。</p>

<p>しかし実際の金融市場では、情報が全銘柄・全市場へ同時に伝わるとは限らない。情報拡散、投資家の注意制約、取引制度、そして特に<strong>市場ごとの取引時間の非同期性</strong>によって、予測可能性が生じる可能性がある。</p>

<p>本研究が着目するのが<strong>リード・ラグ効果</strong>である。これは、ある市場・資産・業種で先に生じた価格変化が、別の市場・資産へ時間差を伴って伝播する現象である。</p>

<p>日米市場では特に、</p>

<p><strong>米国市場クローズ → 日本市場翌営業日オープン</strong></p>

<p>という時間的な順序が存在する。そのため、米国市場で確定した業種別情報が、日本市場の翌営業日の価格形成に利用される可能性がある。</p>

<p>従来のPCAは共通変動を抽出するのに有効だが、60営業日のような比較的短いローリングウィンドウでは、相関行列の固有空間が不安定になりやすい。そこで本研究では、経済的に意味のある事前部分空間を導入して推定を安定化する。</p>

<h1 id="methonds-back"><strong>Methonds</strong> <a href="#top">back</a><a name="method"></a></h1>

<h2 id="1-投資対象">1. 投資対象</h2>

<p>米国側はS&amp;P 500の11業種に対応するSelect Sector SPDR ETFを使用する。</p>

<ul>
  <li>XLB：Materials</li>
  <li>XLE：Energy</li>
  <li>XLF：Financials</li>
  <li>XLI：Industrials</li>
  <li>XLK：Information Technology</li>
  <li>XLP：Consumer Staples</li>
  <li>XLU：Utilities</li>
  <li>XLV：Health Care</li>
  <li>XLY：Consumer Discretionary</li>
  <li>XLC：Communication Services</li>
  <li>XLRE：Real Estate</li>
</ul>

<p>日本側はTOPIX-17に対応するNEXT FUNDS TOPIX-17業種別ETFの17銘柄を使用する。価格は日次Open/Closeであり、株式分割・分配金の影響を調整した価格系列からリターンを作成する。サンプル期間は<strong>2010年1月～2025年12月</strong>であり、日米双方で観測可能な営業日のみを使用する。</p>

<h2 id="2-リターン">2. リターン</h2>

<p>PCAの推定には米国・日本双方の <strong>Close-to-Close return</strong>、
<br /></p>
<div align="center">
$$
r^{cc}_{i,t} = \frac{P^{close}_{i,t}} {P^{close}_{i,t-1}}-1
$$
</div>
<p><br />
を用いる。</p>

<p>一方、投資戦略の評価では、日本市場の翌営業日の日中リターン、
<br /></p>
<div align="center">
$$
r^{oc}_{j,t} = \frac{P^{close}_{j,t}} {P^{open}_{j,t}}-1
$$
</div>
<p><br />
を使用する。</p>

<p>つまり、</p>

<p><strong>米国の当日Close-to-Close → 日本の翌営業日Open-to-Close</strong></p>

<p>という予測関係を構築している。</p>

<h2 id="3-部分空間正則化">3. 部分空間正則化</h2>

<p>推定ウィンドウは<strong>60営業日（L=60）</strong>である。</p>

<p>事前部分空間は <strong>K₀=3</strong> とし、</p>

<ol>
  <li><strong>Global factor</strong></li>
  <li><strong>Country spread factor</strong></li>
  <li><strong>Cyclical–defensive factor</strong></li>
</ol>

<p>の3方向を設定する。</p>

<p>さらに、
\(C_t^{reg}=(1-\lambda)C_t+\lambda C_0\)
として、観測された相関行列
\(C_t\)
を事前相関構造
\(C_0\)
に縮約する。本研究では
\(\lambda=0.9\)
としている。</p>

<p>そのうえで固有分解し、上位
\(K=3\)
個の固有ベクトルを採用する。</p>

<p>この方法の重要な点は、<strong>各固有ベクトルを個別に事前ベクトルへ近づけるのではなく、複数の固有ベクトルが張る「部分空間」そのものを事前部分空間へ近づける</strong>ことにある。</p>

<h2 id="4-リードラグシグナル">4. リード・ラグシグナル</h2>

<p>米国の標準化リターンを
\(z_{U,t}\)
とし、米国側の固有ベクトルを
\(V_{U,t}^{(K)}\)
とすると、
\(f_t=V_{U,t}^{(K)\top}z_{U,t}\)
によって米国ショックを3次元の共通ファクターへ圧縮する。</p>

<p>その後、日本側固有ベクトル
\(V_{J,t}^{(K)}\)
を用いて、
\(\hat z_{J,t+1} = V_{J,t}^{(K)}f_t\)
と復元する。</p>

<p>したがって、
\(\hat z_{J,t+1} = B_t^{(K)}z_{U,t}\)
と書け、
\(B_t^{(K)} = V_{J,t}^{(K)} V_{U,t}^{(K)\top}\)
となる。</p>

<p>この
\(B_t^{(K)}\)
は、<strong>米国業種のショックが日本業種へどのように伝播するかを表す時変の低ランク伝播行列</strong>と解釈できる。さらに、
\(rank(B_t^{(K)})\leq K\)
なので、3次元の低ランク線形予測器となる。</p>

<h2 id="5-ポートフォリオ">5. ポートフォリオ</h2>

<p>シグナル上位30%をロング、下位30%をショートとする。</p>

<p>したがって、
\(q=0.3\)
であり、ロング・ショートとも等ウェイトで構成し、
\(\sum_jw_{j,t}=0, \qquad \sum_j|w_{j,t}|=2\)
とする。</p>

<h2 id="6-比較対象">6. 比較対象</h2>

<p>以下の4戦略を比較した。</p>

<ul>
  <li><strong>MOM</strong>：日本側60営業日の単純モメンタム</li>
  <li><strong>PCA PLAIN</strong>：正則化なしPCA</li>
  <li><strong>PCA SUB</strong>：提案する部分空間正則化PCA</li>
  <li><strong>DOUBLE</strong>：MOMとPCA SUBを2×2段階ソート</li>
</ul>

<p>PCA SUBの事前相関構造 CfullC_{full} の推定には、<strong>2010年1月1日～2014年12月31日</strong>のデータを使用している。</p>

<h1 id="results-back"><strong>Results</strong> <a href="#top">back</a><a name="result"></a></h1>

<p>最も重要な結果は以下である。</p>

<table>
  <thead>
    <tr>
      <th>Strategy</th>
      <th>AR (%)</th>
      <th>RISK (%)</th>
      <th>R/R</th>
      <th>MDD (%)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>MOM</td>
      <td>5.63</td>
      <td>10.59</td>
      <td>0.53</td>
      <td>16.97</td>
    </tr>
    <tr>
      <td>PCA PLAIN</td>
      <td>6.24</td>
      <td>9.94</td>
      <td>0.62</td>
      <td>23.65</td>
    </tr>
    <tr>
      <td><strong>PCA SUB</strong></td>
      <td><strong>23.79</strong></td>
      <td><strong>10.70</strong></td>
      <td><strong>2.22</strong></td>
      <td><strong>9.58</strong></td>
    </tr>
    <tr>
      <td>DOUBLE</td>
      <td>18.86</td>
      <td>11.16</td>
      <td>1.69</td>
      <td>12.10</td>
    </tr>
  </tbody>
</table>

<p>AR: Anual Return, RISK: σ, R/R: Return/Risk, MDD: Maximum Draw Down</p>

<p>PCA SUBは、MOMのAR <strong>5.63%</strong>、PCA PLAINの <strong>6.24%</strong>に対して、<strong>23.79%</strong>という大幅に高い年率リターンを示した。</p>

<p>しかもリスクは <strong>10.70%</strong> であり、MOMの10.59%、PCA PLAINの9.94%と大きく変わらない。</p>

<p>結果としてR/Rは <strong>2.22</strong> と、MOMの0.53、PCA PLAINの0.62、DOUBLEの1.69を大きく上回る。</p>

<p>さらにMDDも <strong>9.58%</strong> と最小である。</p>

<h2 id="ファクター調整後">ファクター調整後</h2>

<p>Fama–French 3ファクターでは、PCA SUBの年率アルファは</p>

<p><strong>22.17%（t=6.69）</strong></p>

<p>であり、1%水準で有意である。</p>

<p>Carhart 4ファクターでも、</p>

<p><strong>22.23%（t=6.73）</strong></p>

<p>であり、こちらも1%水準で有意である。</p>

<p>さらにCarhart 4ファクターに対するPCA SUBのWML係数は</p>

<p><strong>−0.047</strong></p>

<p>で、10%水準で有意である。</p>

<p>一方、MOMではHMLが <strong>0.103</strong>、WMLが <strong>0.268</strong> と正で有意であり、MOMの収益には既知のモメンタム要因がかなり含まれている。</p>

<p>PCA SUBのAdjusted R²はFama–French 3で <strong>0.004</strong>、Carhart 4で <strong>0.006</strong> と非常に小さい。このことから、PCA SUBの収益変動の大部分は従来の株式スタイルファクターでは説明されないことが示唆される。</p>

<h1 id="discussion-back"><strong>Discussion</strong> <a href="#top">back</a><a name="result"></a></h1>

<p>本研究の結果から重要なのは、単に「PCAを使えば予測できる」ということではない。</p>

<p><strong>正則化なしPCAでは十分な性能が得られず、経済的に意味のある部分空間へ正則化することで性能が大幅に改善した</strong>点が重要である。</p>

<p>PCA PLAINはAR <strong>6.24%</strong>、R/R <strong>0.62</strong>、MDD <strong>23.65%</strong>であるのに対し、PCA SUBではAR <strong>23.79%</strong>、R/R <strong>2.22</strong>、MDD <strong>9.58%</strong>となった。</p>

<p>これは、60営業日のローリングウィンドウでは、通常のPCAによる固有空間推定がノイズの影響を受けやすい一方、事前部分空間への縮約によって推定誤差を抑えられるためと解釈されている。</p>

<p>また、提案手法は単なる「米国株が上がったら日本株も買う」という市場方向性の予測ではない。</p>

<p>米国11業種の情報を3つの共通モードへ圧縮し、そのショックを日本17業種へ再配分している。そのため、<strong>市場全体の方向ではなく、業種間の相対的な情報伝播</strong>を利用する構造になっている。</p>

<p>理論面でも、著者らは共通ファクターが米国で先に観測され、翌日に日本へ伝播する理想化モデルを設定し、その場合に提案される低ランク予測器が最良線形予測器と一致することを示している。</p>

<h1 id="novelty-compared-to-previous-studies"><strong>Novelty compared to previous studies</strong></h1>

<p>本研究の新規性は主に4点に整理できる。</p>

<p><strong>1. 日米の取引時間差を直接的な投資シグナルにした点</strong></p>

<p>従来のリード・ラグ研究では情報伝播現象そのものを検証するものも多いが、本研究では、</p>

<p><strong>米国当日 → 日本翌営業日日中</strong></p>

<p>という時間構造を明示的に投資戦略へ落とし込んでいる。</p>

<p><strong>2. 日米業種を同時に扱うPCA</strong></p>

<p>米国と日本を別々に分析するのではなく、日米の業種を結合した相関構造から共通ファクターを抽出している。</p>

<p><strong>3. 部分空間正則化PCAの導入</strong></p>

<p>グローバル、国スプレッド、シクリカル・ディフェンシブという経済的事前知識を部分空間として導入し、通常のPCAの推定不安定性を抑えている。</p>

<p><strong>4. 低ランク線形予測器としての理論的解釈</strong></p>

<p>シグナルを単なる経験的スコアではなく、
\(B_t^{(K)}z_{U,t}\)
という<strong>米国から日本への低ランク伝播行列</strong>として定式化している。</p>

<p>さらに理想化モデルでは最良線形予測器との対応まで示している点が理論的な特徴である。</p>

<h1 id="limitations"><strong>Limitations</strong></h1>

<p>論文の結果から考えると、いくつか重要な限界がある。</p>

<p><strong>1. 理想化モデルと現実市場には乖離がある</strong></p>

<p>命題2は共通ファクターが米国で実現して翌日に日本へ伝播するというモデルを仮定している。これは理論的な裏付けとして有用だが、現実の情報伝播が常にこの構造に従うことを意味しない。</p>

<p><strong>2. 事前部分空間の設定に依存する</strong></p>

<p>3つのファクターとして「グローバル」「国スプレッド」「シクリカル・ディフェンシブ」を設定し、さらに <strong>K=3、λ=0.9</strong> としている。これらの選択が異なれば結果が変わる可能性がある。</p>

<p><strong>3. シクリカル／ディフェンシブの分類には事前判断が入る</strong></p>

<p>例えば米国ではXLB、XLE、XLF、XLREをシクリカル、XLK、XLP、XLU、XLVをディフェンシブとしている。日本側でも特定のETFをあらかじめ分類しているため、完全にデータ駆動型の手法ではない。</p>

<p><strong>4. 取引コスト等の実運用上の問題</strong></p>

<p>論文では高いバックテスト性能が示されているが、実際の運用では売買手数料、Bid-Ask spread、マーケットインパクト、空売りコスト、為替、約定価格の乖離などが発生する。したがって、AR <strong>23.79%</strong> がそのまま実運用収益になるとは解釈できない。</p>

<p><strong>5. 日米市場の祝日・取引日の制約</strong></p>

<p>分析対象は日米双方で共通に観測可能な営業日のみに限定されている。この処理はリークを避けるうえで合理的だが、利用可能なサンプルを減少させる。</p>

<h1 id="potential-applications"><strong>Potential Applications</strong></h1>

<p>最も直接的な応用は、<strong>日米株式市場を利用した短期クロスボーダー・セクターローテーション戦略</strong>である。</p>

<p>例えば米国市場終了後に、</p>

<ol>
  <li>11業種の米国ETFの当日リターンを取得</li>
  <li>60営業日のローリングウィンドウで標準化</li>
  <li>部分空間正則化PCAを更新</li>
  <li>米国ショックを3次元ファクターへ射影</li>
  <li>日本17業種へシグナルを復元</li>
  <li>上位30%をロング、下位30%をショート</li>
  <li>翌営業日の日本市場Open-to-Closeで決済</li>
</ol>

<p>という運用が考えられる。</p>

<p>また、単純な投資戦略だけでなく、<strong>国際的な情報伝播の分析、セクター間のリード・ラグ構造の可視化、クロスマーケット予測、低ランク回帰モデル、金融時系列における構造的正則化</strong>にも応用可能である。</p>

<p>特に本研究の重要なメッセージは、<strong>「PCAで共通因子を探す」だけではなく、「経済的に意味のある部分空間をあらかじめ与え、その上で共通因子を安定的に推定する」ことが予測性能の向上につながる</strong>という点にある。</p>

<p>なお、論文中の図は<strong>Figure 1「提案手法による正則化のイメージ」</strong>と<strong>Figure 2「各戦略の累積リターン」</strong>の2点である。Figure 1については、凡例ではサブパネルを個別に区分しておらず、<strong>左側が通常のベクトル正則化、右側が本研究の部分空間正則化</strong>という構成で説明されている。Figure 2についても、凡例上は個別のa/b等のサブセクションは設定されておらず、「各戦略の累積リターン」を示す図として記載されている。</p>
<h6><p>
[**SciDraft (Japanese)**](https://note.com/daichi_konno/n/nc941e5eacfb?app_launch=false)を利用すると、**研究室での進捗スライドや作成済みの研究計画書から論文ドラフトを執筆**することが可能である。
上記リンク先記事にURLが載っているので、ぜひお試しいただきたい。
また、[**Readable**](https://readable.jp/pricing)を利用すると、**論文PDFのレイアウトを保ったまま日本語への翻訳**が可能である。
クーポンコード"**PaperInterpreter**"の入力で1ヶ月間フリートライアルできるので、こちらもぜひお試しいただきたい😊</p></h6>

<hr />

<p>この論文でいう<strong>「単純モメンタム（MOM）」</strong>と<strong>「モメンタムとのダブルソート（DOUBLE）」</strong>は、どちらも日本の17業種ETFを「過去の値動きが良かったか／悪かったか」で分類して、ロング・ショートする戦略である。</p>

<h2 id="1-単純モメンタムmom">1. 単純モメンタム（MOM）</h2>

<p>一言でいうと、</p>

<blockquote>
  <p><strong>「最近上がっている業種を買い、最近下がっている業種を売る」</strong></p>
</blockquote>

<p>という非常にオーソドックスなモメンタム戦略である。</p>

<p>この論文では、日本の17業種について<strong>過去60営業日のリターン</strong>を計算し、それをモメンタム指標として利用する。</p>

<p>例えば、17業種の過去60営業日リターンが、</p>

<table>
  <thead>
    <tr>
      <th>業種</th>
      <th style="text-align: right">過去60日リターン</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>情報・通信</td>
      <td style="text-align: right">+15%</td>
    </tr>
    <tr>
      <td>電気・ガス</td>
      <td style="text-align: right">+12%</td>
    </tr>
    <tr>
      <td>機械</td>
      <td style="text-align: right">+8%</td>
    </tr>
    <tr>
      <td>…</td>
      <td style="text-align: right">…</td>
    </tr>
    <tr>
      <td>鉄鋼</td>
      <td style="text-align: right">−8%</td>
    </tr>
    <tr>
      <td>鉱業</td>
      <td style="text-align: right">−11%</td>
    </tr>
  </tbody>
</table>

<p>だったとする。</p>

<p>この場合、</p>

<ul>
  <li><strong>上位30%</strong> → ロング</li>
  <li><strong>下位30%</strong> → ショート</li>
</ul>

<p>とする。</p>

<p>17業種なので、概念的には</p>

<p><strong>過去60日で強かった業種</strong>
→ 翌日のOpenで買う</p>

<p><strong>過去60日で弱かった業種</strong>
→ 翌日のOpenで空売りする</p>

<p>という構造である。</p>

<p>したがってMOMは、<strong>米国市場の情報をまったく使わない</strong>。</p>

<p>ここがこの論文では非常に重要である。</p>

<hr />

<h1 id="2-モメンタムとのダブルソートdouble">2. モメンタムとのダブルソート（DOUBLE）</h1>

<p>こちらは少し複雑である。</p>

<p>基本的な発想は、</p>

<blockquote>
  <p><strong>「米国→日本のリード・ラグシグナル」と「日本の過去60日モメンタム」の両方を使って銘柄を選ぶ</strong></p>
</blockquote>

<p>というものである。</p>

<p>つまり、1つの指標だけで選ぶのではなく、<strong>2段階で分類する</strong>。</p>

<h3 id="第1段階pca-subのシグナルで分類">第1段階：PCA SUBのシグナルで分類</h3>

<p>まず、論文の提案手法であるPCA SUBによって、</p>

<blockquote>
  <p>「米国の今日の動きから考えると、明日の日本のどの業種が上がりそうか」</p>
</blockquote>

<p>という予測シグナルを17業種について計算する。</p>

<p>そして、</p>

<ul>
  <li>PCA SUBシグナルが高い</li>
  <li>PCA SUBシグナルが低い</li>
</ul>

<p>というように分類する。</p>

<h3 id="第2段階モメンタムでさらに分類">第2段階：モメンタムでさらに分類</h3>

<p>そのうえで、それぞれのグループの中で、</p>

<p><strong>過去60営業日のモメンタム</strong></p>

<p>を使ってさらに分類する。</p>

<p>つまり、</p>

<p><strong>PCA SUB × MOM</strong></p>

<p>の2つの情報を組み合わせる。</p>

<hr />

<h2 id="イメージするとこうなる">イメージするとこうなる</h2>

<p>例えば17業種をまずPCA SUBで、</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PCA SUBシグナル

高い
│
├── A
├── B
├── C
├── D
├── E
├── F
├── G
│

低い
│
├── H
├── I
├── J
├── K
├── L
├── M
└── N
</code></pre></div></div>

<p>のように分ける。</p>

<p>さらに、それぞれについてモメンタムを見る。</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                 過去60日モメンタム
                 強い       弱い

PCA SUB 高い     A/B/C      D/E/F
PCA SUB 低い     G/H/I      J/K/L
</code></pre></div></div>

<p>というような<strong>2×2の分類</strong>を作るイメージである。</p>

<p>そして、</p>

<p><strong>PCA SUBが強い ＋ モメンタムも強い</strong></p>

<p>という業種を特にロング側へ、</p>

<p><strong>PCA SUBが弱い ＋ モメンタムも弱い</strong></p>

<p>という業種を特にショート側へ、</p>

<p>というように選別する。</p>

<p>これが「ダブルソート」である。</p>

<hr />

<h1 id="3-momとdoubleの違い">3. MOMとDOUBLEの違い</h1>

<p>非常に簡単にまとめると、</p>

<table>
  <thead>
    <tr>
      <th>戦略</th>
      <th>何を見るか</th>
      <th>考え方</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>MOM</strong></td>
      <td>日本の過去60日リターンだけ</td>
      <td>「最近強い業種を買う」</td>
    </tr>
    <tr>
      <td><strong>DOUBLE</strong></td>
      <td>PCA SUB＋過去60日モメンタム</td>
      <td>「米国からの予測シグナルと日本のモメンタムが両方強い業種を買う」</td>
    </tr>
    <tr>
      <td><strong>PCA SUB</strong></td>
      <td>米国業種→日本業種のリード・ラグ</td>
      <td>「米国の今日の動きから明日の日本業種を予測する」</td>
    </tr>
  </tbody>
</table>

<p>したがって、研究上の比較としては、</p>

<p><strong>MOM</strong>
→ 従来型のモメンタムだけでどれくらい儲かるか</p>

<p><strong>PCA SUB</strong>
→ 日米のリード・ラグだけでどれくらい儲かるか</p>

<p><strong>DOUBLE</strong>
→ その2つを組み合わせたらさらに良くなるか</p>

<p>という関係になっている。</p>

<hr />

<h1 id="4-ところがこの論文では面白い結果になっている">4. ところが、この論文では面白い結果になっている</h1>

<p>結果を見ると、</p>

<ul>
  <li><strong>MOM：年率リターン 5.63%</strong></li>
  <li><strong>PCA SUB：23.79%</strong></li>
  <li><strong>DOUBLE：18.86%</strong></li>
</ul>

<p>である。</p>

<p>つまり、</p>

<p><strong>MOM &lt; DOUBLE &lt; PCA SUB</strong></p>

<p>となっている。</p>

<p>これはかなり興味深い。</p>

<p>普通に考えると、</p>

<blockquote>
  <p>「PCA SUBにモメンタムまで追加したら、もっと良くなるのでは？」</p>
</blockquote>

<p>と思う。</p>

<p>しかし実際には、<strong>PCA SUB単独の23.79%のほうが、DOUBLEの18.86%より高い</strong>。</p>

<p>したがって、この研究では、</p>

<blockquote>
  <p><strong>日本市場の過去60日モメンタムを追加することが、米国→日本のリード・ラグ情報の価値を高めるとは限らない</strong></p>
</blockquote>

<p>という結果になっている。</p>

<p>むしろPCA SUBは、従来型のモメンタムとは異なる情報を捉えている可能性が高い。</p>

<p>特にCarhart 4ファクター分析で、PCA SUBのWML係数が<strong>−0.047</strong>であることも、この「PCA SUBは単純なモメンタムとは違う」という点を裏付ける重要な結果である。</p>]]></content><author><name>Tana Gone</name></author><category term="Trading" /><summary type="html"><![CDATA[投資戦略を紹介する動画で紹介された論文を論文解説WebサイトのAIに解説してもらった。]]></summary></entry><entry><title type="html">EBライブラリをSwiftから</title><link href="/swiftui/ebswift/" rel="alternate" type="text/html" title="EBライブラリをSwiftから" /><published>2026-08-19T18:59:02+09:00</published><updated>2026-08-19T18:59:02+09:00</updated><id>/swiftui/ebswift</id><content type="html" xml:base="/swiftui/ebswift/"><![CDATA[<p>EPWING仕様の電子辞書をC言語からアクセスするためのEBライブラリが2001-2005に開発された。開発者は笠原基之(かさはら もとゆき)氏で、時代を反映してライブラリが出力する日本語はEUC-JPである。ライブラリが受け取る日本語もEUC-JPである。ライブラリ付属のサンプルファイル群からtext.cを取出しSwift言語へ移植してみた。</p>

<p><a href="https://www.mistys-internet.website/eb/">EB ライブラリ</a>
<br />
<a href="http://openlab.jp/edict/eblook/eblook.html">eblook</a>
<br />
<a href="http://ikazuhiro.s206.xrea.com/">Kazuhiro’s blog</a>
<br />
<a href="http://green.ribbon.to/~ikazuhiro/dic/ebu.html">utf8 version eb lib</a>
<br />
<a href="http://charset.7jp.net/">日本語文字コード</a></p>

<h3 id="textcソースに全角外字フック関数を追加">text.cソースに全角外字フック関数を追加</h3>

<p>オリジナルのtext.cにはフック関数の登録は無く、全角外字は&lt;?&gt;と表示される。フック関数を登録すると任意の文字列(EUC-JP)へ表示を変更できる。</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"># hook.swift</span>
<span class="kd">import</span> <span class="kt">Foundation</span>

<span class="k">let</span> <span class="nv">MAXLEN_TEXT</span> <span class="o">=</span> <span class="mi">1023</span>

<span class="k">var</span> <span class="nv">error_code</span><span class="p">:</span> <span class="kt">EB_Error_Code</span>
<span class="k">var</span> <span class="nv">book</span><span class="p">:</span> <span class="kt">EB_Book</span> <span class="o">=</span> <span class="kt">EB_Book</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">subbook_list</span> <span class="o">=</span> <span class="p">[</span><span class="kt">EB_Subbook_Code</span><span class="p">](</span><span class="nv">repeating</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="kt">EB_MAX_SUBBOOKS</span><span class="p">))</span>
<span class="k">var</span> <span class="nv">subbook_count</span><span class="p">:</span> <span class="kt">Int32</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">var</span> <span class="nv">subbook_index</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">var</span> <span class="nv">text_position</span><span class="p">:</span> <span class="kt">EB_Position</span> <span class="o">=</span> <span class="kt">EB_Position</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">text</span> <span class="o">=</span> <span class="p">[</span><span class="kt">CChar</span><span class="p">](</span><span class="nv">repeating</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">MAXLEN_TEXT</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">text_length</span><span class="p">:</span> <span class="n">ssize_t</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">var</span> <span class="nv">text_count</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">var</span> <span class="nv">i</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>

<span class="k">let</span> <span class="nv">argv</span> <span class="o">=</span> <span class="kt">ProcessInfo</span><span class="o">.</span><span class="n">processInfo</span><span class="o">.</span><span class="n">arguments</span>
<span class="k">let</span> <span class="nv">argc</span> <span class="o">=</span> <span class="n">argv</span><span class="o">.</span><span class="n">count</span>
<span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span> <span class="o">..&lt;</span> <span class="n">argv</span><span class="o">.</span><span class="n">count</span> <span class="p">{</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"argv[</span><span class="se">\(</span><span class="n">i</span><span class="se">)</span><span class="s">] = </span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>

<span class="cm">/* エラー発生で終了するときの処理。*/</span>
<span class="kd">func</span> <span class="nf">die</span><span class="p">()</span> <span class="p">{</span>
  <span class="nf">eb_finalize_book</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">)</span>
  <span class="nf">eb_finalize_library</span><span class="p">()</span>
  <span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="cm">/* コマンド行引数をチェック。*/</span>
<span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"Usage: </span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s"> book-path subbook-index number</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
  <span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">text_count</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="nf">atoi</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">3</span><span class="p">]))</span>

<span class="cm">/* EB ライブラリと `book' を初期化。*/</span>
<span class="nf">eb_initialize_library</span><span class="p">()</span>
<span class="nf">eb_initialize_book</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">)</span>

<span class="cm">/* 書籍を `book' に結び付ける。*/</span>
<span class="n">error_code</span> <span class="o">=</span> <span class="nf">eb_bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">!=</span> <span class="kt">EB_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s">: failed to bind the book, </span><span class="se">\(</span><span class="nf">eb_error_message</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span><span class="o">!</span><span class="se">)</span><span class="s">: </span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
  <span class="nf">die</span><span class="p">()</span>
<span class="p">}</span>

<span class="cm">/* 副本の一覧を取得。*/</span>
<span class="n">error_code</span> <span class="o">=</span> <span class="nf">eb_subbook_list</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">subbook_list</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">subbook_count</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">!=</span> <span class="kt">EB_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s">: failed to get the subbbook list, </span><span class="se">\(</span><span class="nf">eb_error_message</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span><span class="o">!</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
  <span class="nf">die</span><span class="p">()</span>
<span class="p">}</span>

<span class="cm">/* 副本のインデックスを取得。*/</span>
<span class="n">subbook_index</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="nf">atoi</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span>

<span class="cm">/*「現在の副本 (current subbook)」を設定。*/</span>
<span class="n">error_code</span> <span class="o">=</span> <span class="nf">eb_set_subbook</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">,</span> <span class="n">subbook_list</span><span class="p">[</span><span class="n">subbook_index</span><span class="p">])</span>
<span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">!=</span> <span class="kt">EB_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s">: failed to set the current subbook, </span><span class="se">\(</span><span class="nf">eb_error_message</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span><span class="o">!</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
  <span class="nf">die</span><span class="p">()</span>
<span class="p">}</span>

<span class="cm">/* テキストの開始位置を取得。*/</span>
<span class="n">error_code</span> <span class="o">=</span> <span class="nf">eb_text</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">text_position</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">!=</span> <span class="kt">EB_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s">: failed to get text information, </span><span class="se">\(</span><span class="nf">eb_error_message</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span><span class="o">!</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
  <span class="nf">die</span><span class="p">()</span>
<span class="p">}</span>

<span class="cm">/* テキストをシーク。*/</span>
<span class="n">error_code</span> <span class="o">=</span> <span class="nf">eb_seek_text</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">text_position</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">!=</span> <span class="kt">EB_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s">: failed to seek text, </span><span class="se">\(</span><span class="nf">eb_error_message</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span><span class="o">!</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
  <span class="nf">die</span><span class="p">()</span>
<span class="p">}</span>

<span class="c1">// 1. hookset の初期化</span>
<span class="k">var</span> <span class="nv">hookset</span> <span class="o">=</span> <span class="kt">EB_Hookset</span><span class="p">()</span>
<span class="nf">eb_initialize_hookset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hookset</span><span class="p">)</span>

<span class="c1">// 2. フック関数の定義 (@convention(c) を指定してC言語の関数ポインタとして扱う)</span>
<span class="k">let</span> <span class="nv">hook_widefont</span><span class="p">:</span> <span class="kd">@convention</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="p">(</span>
    <span class="kt">UnsafeMutablePointer</span><span class="o">&lt;</span><span class="kt">EB_Book</span><span class="o">&gt;</span><span class="p">?,</span> 
    <span class="kt">UnsafeMutablePointer</span><span class="o">&lt;</span><span class="kt">EB_Appendix</span><span class="o">&gt;</span><span class="p">?,</span> 
    <span class="kt">UnsafeMutableRawPointer</span><span class="p">?,</span> 
    <span class="kt">EB_Hook_Code</span><span class="p">,</span> 
    <span class="kt">Int32</span><span class="p">,</span> 
    <span class="kt">UnsafePointer</span><span class="o">&lt;</span><span class="kt">UInt32</span><span class="o">&gt;</span><span class="p">?</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">EB_Error_Code</span> <span class="o">=</span> <span class="p">{</span> <span class="n">book</span><span class="p">,</span> <span class="n">appendix</span><span class="p">,</span> <span class="n">container</span><span class="p">,</span> <span class="n">code</span><span class="p">,</span> <span class="n">argc</span><span class="p">,</span> <span class="n">argv</span> <span class="k">in</span>
    
  <span class="c1">// 💡 "全角外字EUC-JP の CChar 配列に変換</span>
  <span class="c1">// let gaijiNum: UInt32 = argv![0]</span>
  <span class="k">let</span> <span class="nv">gaijiNum</span><span class="p">:</span> <span class="kt">UInt32</span> <span class="o">=</span> <span class="n">argv</span><span class="o">!.</span><span class="n">pointee</span>
  <span class="nf">print</span><span class="p">(</span><span class="s">"gaijiNum = </span><span class="se">\(</span><span class="n">gaijiNum</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
  <span class="k">if</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="s">"&lt;gaiji&gt;</span><span class="se">\(</span><span class="n">gaijiNum</span><span class="se">)</span><span class="s">&lt;/gaiji&gt;"</span><span class="o">.</span><span class="nf">data</span><span class="p">(</span><span class="nv">using</span><span class="p">:</span> <span class="o">.</span><span class="n">japaneseEUC</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">cstr</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="kt">CChar</span><span class="p">(</span><span class="nv">bitPattern</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
    <span class="n">cstr</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1">// ヌル終端</span>

      <span class="c1">// 💡 cstr を UnsafePointer&lt;CChar&gt; として一時的に取得し、関数に渡す</span>
      <span class="n">cstr</span><span class="o">.</span><span class="n">withUnsafeBufferPointer</span> <span class="p">{</span> <span class="n">buffer</span> <span class="k">in</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">baseAddress</span> <span class="o">=</span> <span class="n">buffer</span><span class="o">.</span><span class="n">baseAddress</span> <span class="p">{</span>
          <span class="nf">eb_write_text_string</span><span class="p">(</span><span class="n">book</span><span class="p">,</span> <span class="n">baseAddress</span><span class="p">)</span>
        <span class="p">}</span>
      <span class="p">}</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="c1">// エンコード失敗時のフォールバック（必要に応じて）</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"EUC-JP encoding failed for &lt;br&gt;"</span><span class="p">)</span>
  <span class="p">}</span>

  <span class="c1">// C言語の return 0; に相当。EB_SUCCESS（0）を返す</span>
  <span class="k">return</span> <span class="kt">EB_SUCCESS</span> 
<span class="p">}</span>

<span class="c1">// 3. hook の設定と登録</span>
<span class="k">var</span> <span class="nv">hook</span> <span class="o">=</span> <span class="kt">EB_Hook</span><span class="p">()</span>
<span class="n">hook</span><span class="o">.</span><span class="n">code</span> <span class="o">=</span> <span class="kt">EB_HOOK_WIDE_FONT</span>
<span class="n">hook</span><span class="o">.</span><span class="n">function</span> <span class="o">=</span> <span class="n">hook_widefont</span>

<span class="nf">eb_set_hook</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hookset</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hook</span><span class="p">)</span>

<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">text_count</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* テキストを取得。appendix, hookset, container*/</span>
  <span class="n">error_code</span> <span class="o">=</span> <span class="nf">eb_read_text</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hookset</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="kt">Int</span><span class="p">(</span><span class="kt">MAXLEN_TEXT</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">text</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">text_length</span><span class="p">)</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">!=</span> <span class="kt">EB_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s">: failed to read text, </span><span class="se">\(</span><span class="nf">eb_error_message</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span><span class="o">!</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
    <span class="nf">die</span><span class="p">()</span>
  <span class="p">}</span>
  <span class="c1">// fputs(text, stdout)</span>
  <span class="k">if</span> <span class="k">let</span> <span class="nv">str</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="nv">cString</span><span class="p">:</span> <span class="n">text</span><span class="p">,</span> <span class="nv">encoding</span><span class="p">:</span> <span class="o">.</span><span class="n">japaneseEUC</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">i</span><span class="se">)</span><span class="s">: </span><span class="se">\(</span><span class="n">str</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
  <span class="p">}</span>

  <span class="k">if</span> <span class="p">(</span><span class="nf">eb_is_text_stopped</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">continue</span>
  <span class="p">}</span>

  <span class="nf">fputs</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">----------------------------------------</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">stdout</span><span class="p">)</span>

  <span class="cm">/* 次の単語の説明へ移動。*/</span>
  <span class="n">error_code</span> <span class="o">=</span> <span class="nf">eb_forward_text</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">==</span> <span class="kt">EB_ERR_END_OF_CONTENT</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">fputs</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">[END]</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">stdout</span><span class="p">)</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">error_code</span> <span class="o">!=</span> <span class="kt">EB_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="se">)</span><span class="s">: failed to read text, </span><span class="se">\(</span><span class="nf">eb_error_message</span><span class="p">(</span><span class="n">error_code</span><span class="p">)</span><span class="o">!</span><span class="se">)\n</span><span class="s">"</span><span class="p">)</span>
    <span class="nf">die</span><span class="p">()</span>
  <span class="p">}</span>
  <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="p">}</span>

<span class="cm">/* 書籍と EB ライブラリの利用を終了。*/</span>
<span class="nf">eb_finalize_hookset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hookset</span><span class="p">)</span>
<span class="nf">eb_finalize_book</span><span class="p">(</span><span class="o">&amp;</span><span class="n">book</span><span class="p">)</span>
<span class="nf">eb_finalize_library</span><span class="p">()</span>
<span class="nf">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="コンパイルコマンド">コンパイルコマンド</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$swiftc</span> <span class="nt">-L</span>/usr/local/lib <span class="nt">-leb</span> <span class="nt">-I</span>/usr/local/include/eb <span class="nt">-import-objc-header</span> bridging.h hook.swift

<span class="nv">$.</span>/hook <span class="s2">"/Users/foobar/Library/Application Support/EBMac/READERS"</span> 0 10
0: A１, a１ /&lt;?&gt;&lt;?&gt;/→&lt;gaiji&gt;45345&lt;/gaiji&gt;

&lt;gaiji&gt;45349&lt;/gaiji&gt;n, <span class="o">(</span>a<span class="o">)</span> <span class="o">(</span>pl A<span class="s1">'s, As, a'</span>s, as /-z/<span class="o">)</span> エイ《英語アルファベットの第 1 字》<span class="p">;</span> A <span class="o">[</span>a] の表わす音《英語では /&lt;gaiji&gt;45397&lt;/gaiji&gt;/ /&lt;?&gt;&lt;?&gt;/ /e&lt;?&gt;/ /&lt;?&gt;&lt;?&gt;/ /&lt;?&gt;/》<span class="p">;</span> A 字形<span class="o">(</span>のもの<span class="o">)</span><span class="p">;</span> 1 番目<span class="o">(</span>のもの<span class="o">)</span><span class="p">;</span> 《仮定の》第 1, 甲<span class="p">;</span> 【楽】 イ音<span class="o">(</span>の弦[鍵など]<span class="o">)</span>, イ調, 《階名唱法の》ラ<span class="o">(</span>音<span class="o">)</span><span class="p">;</span> 【数】 第 1 既知数<span class="p">;</span> 《等級が》第一の[最上の]<span class="o">(</span>もの<span class="o">)</span><span class="p">;</span> 《学業成績で》 A, 優, 甲<span class="p">;</span> 【紙】 A 判 <span class="o">(</span>⇒→A3, →A4, →A5<span class="o">)</span><span class="p">;</span> 【電算】 《十六進数の》 A 《十進法で 10》.
</code></pre></div></div>

<h3 id="ライブラリフォルダインクルードフォルダ-l--iの中身">ライブラリフォルダ、インクルードフォルダ(-L, -I)の中身</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ls</span> /usr/local/include/eb
appendix.h booklist.h eb.h       font.h     text.h
binary.h   defs.h     error.h    sysdefs.h  zio.h

<span class="nv">$ls</span> /usr/local/lib/lib
Completing file
libeb.16.dylib<span class="k">*</span>         libeb.la<span class="k">*</span>               libta_lib.dylib@
libeb.a                 libta_lib.0.0.0.dylib<span class="k">*</span>
libeb.dylib@            libta_lib.0.dylib@
</code></pre></div></div>]]></content><author><name>Tana Gone</name></author><category term="SwiftUI" /><summary type="html"><![CDATA[EPWING仕様の電子辞書をC言語からアクセスするためのEBライブラリが2001-2005に開発された。開発者は笠原基之(かさはら もとゆき)氏で、時代を反映してライブラリが出力する日本語はEUC-JPである。ライブラリが受け取る日本語もEUC-JPである。ライブラリ付属のサンプルファイル群からtext.cを取出しSwift言語へ移植してみた。]]></summary></entry><entry><title type="html">DashのApple_API_Reference.docsetのコンテンツ</title><link href="/swiftui/dashapple-api-referencedocset/" rel="alternate" type="text/html" title="DashのApple_API_Reference.docsetのコンテンツ" /><published>2026-08-19T15:04:35+09:00</published><updated>2026-08-19T15:04:35+09:00</updated><id>/swiftui/dashapple-api-referencedocset</id><content type="html" xml:base="/swiftui/dashapple-api-referencedocset/"><![CDATA[<p>オフラインで動作するドキュメントViewerアプリDashが表示するコンテンツは、brotli圧縮されたアップルのDocC JSONに格納されている。検索キー(e.g. SwiftUI Framework)からコンテンツの取り出し方を探ってみた。</p>

<p>ref: <a href="https://github.com/nzrsky/appleref-mcp">nzrsky/appleref-mcp: Offline MCP server for Apple Developer Documentation, reading directly from a Dash-format Apple_API_Reference.docset</a></p>

<h3 id="検索キー">検索キー</h3>

<p>DBファイル: Resources/docSet.dsidx
テーブル: searchIndex</p>

<table>
  <thead>
    <tr>
      <th>カラム名</th>
      <th>説明</th>
      <th>例</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>name</td>
      <td>検索キー（クラス名、メソッド名、関数名、ガイド名など）</td>
      <td>UIView, Accelerate, viewDidLoad</td>
    </tr>
    <tr>
      <td>type</td>
      <td>シンボルの種類</td>
      <td>Class, Struct, Method, Property, Framework, Guide等</td>
    </tr>
    <tr>
      <td>path</td>
      <td>ドキュメント表示用のパス・URL</td>
      <td>dash-apple-api://load?…</td>
    </tr>
  </tbody>
</table>

<p>検索キーとシンボルタイプをCSV形式で出力</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sqlite3 <span class="nt">-separator</span> <span class="s1">','</span> docSet.dsidx <span class="se">\</span>
      <span class="s2">"SELECT DISTINCT name, type FROM searchIndex ORDER BY type, name;"</span> <span class="o">&gt;</span>
  search_keys_with_types.csv
</code></pre></div></div>

<h3 id="コンテンツ">コンテンツ</h3>

<p>name=SwiftUI type=Frameworkに対するpath</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">searchIndex</span> <span class="k">where</span> <span class="n">name</span><span class="o">=</span><span class="s1">'SwiftUI'</span> <span class="k">and</span> <span class="k">type</span><span class="o">=</span><span class="s1">'Framework'</span><span class="p">;</span>
<span class="n">id</span><span class="o">|</span><span class="n">name</span><span class="o">|</span><span class="k">type</span><span class="o">|</span><span class="n">path</span>
<span class="mi">14948</span><span class="o">|</span><span class="n">SwiftUI</span><span class="o">|</span><span class="n">Framework</span><span class="o">|</span><span class="n">dash</span><span class="o">-</span><span class="n">apple</span><span class="o">-</span><span class="n">api</span><span class="p">:</span><span class="o">//</span><span class="k">load</span><span class="o">?</span><span class="n">request_key</span><span class="o">=</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">#&lt;</span><span class="n">dash_entry_language</span><span class="o">=</span><span class="n">occ</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_menuDescription</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_name</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;</span>
<span class="mi">14949</span><span class="o">|</span><span class="n">SwiftUI</span><span class="o">|</span><span class="n">Framework</span><span class="o">|</span><span class="n">dash</span><span class="o">-</span><span class="n">apple</span><span class="o">-</span><span class="n">api</span><span class="p">:</span><span class="o">//</span><span class="k">load</span><span class="o">?</span><span class="n">request_key</span><span class="o">=</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">#&lt;</span><span class="n">dash_entry_language</span><span class="o">=</span><span class="n">swift</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_menuDescription</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_name</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;</span>
</code></pre></div></div>

<p>あるいは</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">searchIndex</span> <span class="k">where</span> <span class="n">path</span> <span class="k">like</span> <span class="s1">'%documentation/swiftui%'</span> <span class="k">limit</span> <span class="mi">3</span><span class="p">;</span>
<span class="n">id</span><span class="o">|</span><span class="n">name</span><span class="o">|</span><span class="k">type</span><span class="o">|</span><span class="n">path</span>
<span class="mi">14948</span><span class="o">|</span><span class="n">SwiftUI</span><span class="o">|</span><span class="n">Framework</span><span class="o">|</span><span class="n">dash</span><span class="o">-</span><span class="n">apple</span><span class="o">-</span><span class="n">api</span><span class="p">:</span><span class="o">//</span><span class="k">load</span><span class="o">?</span><span class="n">request_key</span><span class="o">=</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">#&lt;</span><span class="n">dash_entry_language</span><span class="o">=</span><span class="n">occ</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_menuDescription</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_name</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;</span>
<span class="mi">14949</span><span class="o">|</span><span class="n">SwiftUI</span><span class="o">|</span><span class="n">Framework</span><span class="o">|</span><span class="n">dash</span><span class="o">-</span><span class="n">apple</span><span class="o">-</span><span class="n">api</span><span class="p">:</span><span class="o">//</span><span class="k">load</span><span class="o">?</span><span class="n">request_key</span><span class="o">=</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">#&lt;</span><span class="n">dash_entry_language</span><span class="o">=</span><span class="n">swift</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_menuDescription</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_name</span><span class="o">=</span><span class="n">SwiftUI</span><span class="o">&gt;</span>
<span class="mi">14956</span><span class="o">|</span><span class="n">Accessibility</span> <span class="n">fundamentals</span><span class="o">|</span><span class="n">Guide</span><span class="o">|</span><span class="n">dash</span><span class="o">-</span><span class="n">apple</span><span class="o">-</span><span class="n">api</span><span class="p">:</span><span class="o">//</span><span class="k">load</span><span class="o">?</span><span class="n">request_key</span><span class="o">=</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">/</span><span class="n">accessibility</span><span class="o">-</span><span class="n">fundamentals</span><span class="o">#&lt;</span><span class="n">dash_entry_language</span><span class="o">=</span><span class="n">occ</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_menuDescription</span><span class="o">=</span><span class="n">SwiftUI</span><span class="p">.</span><span class="n">Accessibility</span><span class="o">%</span><span class="mi">20</span><span class="n">fundamentals</span><span class="o">&gt;&lt;</span><span class="n">dash_entry_name</span><span class="o">=</span><span class="n">Accessibility</span><span class="o">%</span><span class="mi">20</span><span class="n">fundamentals</span><span class="o">&gt;</span>
</code></pre></div></div>

<p>pathの一部をrequest_keyにしてcacheテーブルを検索するとrequest_key_aliasが得られる</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="k">cache</span> <span class="k">where</span> <span class="n">request_key</span><span class="o">=</span><span class="s1">'ls/documentation/swiftui'</span><span class="p">;</span>
<span class="n">id</span><span class="o">|</span><span class="n">name</span><span class="o">|</span><span class="n">request_key</span><span class="o">|</span><span class="n">inheritance</span><span class="o">|</span><span class="n">usr</span><span class="o">|</span><span class="n">request_key_alias</span>
<span class="mi">10253</span><span class="o">|</span><span class="n">SwiftUI</span><span class="o">|</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">|||</span><span class="n">lsRP1B3qxf</span>
</code></pre></div></div>

<p>cache.dbファイルのrefsテーブルからResources/Document/fs/39にコンテンツが格納されていることがわかる</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">refs</span> <span class="k">where</span> <span class="n">uuid</span><span class="o">=</span><span class="s1">'lsRP1B3qxf'</span><span class="p">;</span>
<span class="n">row_id</span><span class="o">|</span><span class="n">uuid</span><span class="o">|</span><span class="n">data_id</span><span class="o">|</span><span class="k">offset</span><span class="o">|</span><span class="k">length</span>
<span class="mi">10275</span><span class="o">|</span><span class="n">lsRP1B3qxf</span><span class="o">|</span><span class="mi">39</span><span class="o">|</span><span class="mi">0</span><span class="o">|</span><span class="mi">37332</span>
</code></pre></div></div>

<p>または、</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="k">cache</span> <span class="k">where</span> <span class="n">request_key</span><span class="o">=</span><span class="s1">'ls/documentation/swiftui'</span><span class="p">;</span>
<span class="n">id</span><span class="o">|</span><span class="n">name</span><span class="o">|</span><span class="n">request_key</span><span class="o">|</span><span class="n">inheritance</span><span class="o">|</span><span class="n">usr</span><span class="o">|</span><span class="n">request_key_alias</span>
<span class="mi">10253</span><span class="o">|</span><span class="n">SwiftUI</span><span class="o">|</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">|||</span><span class="n">lsRP1B3qxf</span>

<span class="n">sqlite</span><span class="o">&gt;</span> <span class="n">attach</span> <span class="k">database</span> <span class="s1">'cache.db'</span> <span class="k">as</span> <span class="k">cache</span><span class="p">;</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="k">cache</span><span class="p">.</span><span class="n">refs</span> <span class="k">where</span> <span class="n">uuid</span><span class="o">=</span><span class="s1">'lsRP1B3qxf'</span><span class="p">;</span>
<span class="n">row_id</span><span class="o">|</span><span class="n">uuid</span><span class="o">|</span><span class="n">data_id</span><span class="o">|</span><span class="k">offset</span><span class="o">|</span><span class="k">length</span>
<span class="mi">10275</span><span class="o">|</span><span class="n">lsRP1B3qxf</span><span class="o">|</span><span class="mi">39</span><span class="o">|</span><span class="mi">0</span><span class="o">|</span><span class="mi">37332</span>

<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">SELECT</span>
    <span class="k">c</span><span class="p">.</span><span class="n">name</span><span class="p">,</span>
    <span class="k">c</span><span class="p">.</span><span class="n">request_key</span><span class="p">,</span>
    <span class="n">r</span><span class="p">.</span><span class="n">uuid</span><span class="p">,</span>
    <span class="n">r</span><span class="p">.</span><span class="n">data_id</span> <span class="k">AS</span> <span class="n">target_file</span><span class="p">,</span>
    <span class="n">r</span><span class="p">.</span><span class="k">offset</span><span class="p">,</span>
    <span class="n">r</span><span class="p">.</span><span class="k">length</span>
<span class="k">FROM</span>
    <span class="k">cache</span> <span class="k">AS</span> <span class="k">c</span>
<span class="k">INNER</span> <span class="k">JOIN</span>
    <span class="k">cache</span><span class="p">.</span><span class="n">refs</span> <span class="k">AS</span> <span class="n">r</span> <span class="k">ON</span> <span class="k">c</span><span class="p">.</span><span class="n">request_key_alias</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">uuid</span>
<span class="k">WHERE</span>
    <span class="k">c</span><span class="p">.</span><span class="n">request_key</span> <span class="o">=</span> <span class="s1">'ls/documentation/swiftui'</span><span class="p">;</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="k">c</span><span class="p">.</span><span class="n">name</span><span class="p">,</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="k">c</span><span class="p">.</span><span class="n">request_key</span><span class="p">,</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="n">r</span><span class="p">.</span><span class="n">uuid</span><span class="p">,</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="n">r</span><span class="p">.</span><span class="n">data_id</span> <span class="k">AS</span> <span class="n">target_file</span><span class="p">,</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="n">r</span><span class="p">.</span><span class="k">offset</span><span class="p">,</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="n">r</span><span class="p">.</span><span class="k">length</span>
   <span class="p">...</span><span class="o">&gt;</span> <span class="k">FROM</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="k">cache</span> <span class="k">AS</span> <span class="k">c</span>
   <span class="p">...</span><span class="o">&gt;</span> <span class="k">INNER</span> <span class="k">JOIN</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="k">cache</span><span class="p">.</span><span class="n">refs</span> <span class="k">AS</span> <span class="n">r</span> <span class="k">ON</span> <span class="k">c</span><span class="p">.</span><span class="n">request_key_alias</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">uuid</span>
   <span class="p">...</span><span class="o">&gt;</span> <span class="k">WHERE</span>
   <span class="p">...</span><span class="o">&gt;</span>     <span class="k">c</span><span class="p">.</span><span class="n">request_key</span> <span class="o">=</span> <span class="s1">'ls/documentation/swiftui'</span><span class="p">;</span>
<span class="n">name</span><span class="o">|</span><span class="n">request_key</span><span class="o">|</span><span class="n">uuid</span><span class="o">|</span><span class="n">target_file</span><span class="o">|</span><span class="k">offset</span><span class="o">|</span><span class="k">length</span>
<span class="n">SwiftUI</span><span class="o">|</span><span class="n">ls</span><span class="o">/</span><span class="n">documentation</span><span class="o">/</span><span class="n">swiftui</span><span class="o">|</span><span class="n">lsRP1B3qxf</span><span class="o">|</span><span class="mi">39</span><span class="o">|</span><span class="mi">0</span><span class="o">|</span><span class="mi">37332</span>
</code></pre></div></div>

<p>バイナリファイルfs/39はbrotli圧縮ファイルで次のコマンドで解凍できる
<a href="https://github.com/google/brotli/tree/8e10eeb3378f6c459dbaf033ca6727e9816afccb">google/brotli at 8e10eeb3378f6c459dbaf033ca6727e9816afccb</a></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brotli <span class="nt">-dc</span> fs/39 | <span class="nb">dd </span><span class="nv">bs</span><span class="o">=</span>1 <span class="nv">skip</span><span class="o">=</span>0 <span class="nv">count</span><span class="o">=</span>37332 <span class="nv">status</span><span class="o">=</span>none <span class="o">&gt;</span> 39_part.json <span class="c"># DocC JSON</span>
</code></pre></div></div>

<p>DocC JSONの抜粋</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"symbol"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"topicSections"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"anchor"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Essentials"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Essentials"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"identifiers"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"doc://com.apple.documentation/documentation/TechnologyOverviews/adopting-liquid-glass"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"doc://com.apple.documentation/tutorials/swiftui-concepts"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"doc://com.apple.documentation/tutorials/Sample-Apps"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"doc://com.apple.SwiftUI/documentation/SwiftUI/Landmarks-Building-an-app-with-Liquid-Glass"</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
</span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<p>JSONキーとdoc:// URI</p>

<table>
  <thead>
    <tr>
      <th>JSON キー</th>
      <th>内容</th>
      <th>説明</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>metadata</td>
      <td>タイトル、ロール、プラットフォーム</td>
      <td>title: “SwiftUI”, role: “collection” など</td>
    </tr>
    <tr>
      <td>abstract</td>
      <td>概要説明文</td>
      <td>ページの冒頭に表示される要約、テキスト配列</td>
    </tr>
    <tr>
      <td>topicSections</td>
      <td>トピック分類とシンボル一覧</td>
      <td>Essentials, App structure, Views等のグループと所属シンボル識別子</td>
    </tr>
    <tr>
      <td>seeAlsoSections</td>
      <td>関連ドキュメント</td>
      <td>関連するガイドやチュートリアルへのリンク</td>
    </tr>
    <tr>
      <td>primaryContentSections</td>
      <td>本文詳細</td>
      <td>宣言文（Declaration）や詳細な使い方のブロック</td>
    </tr>
  </tbody>
</table>

<p>doc://URI は、ドメイン部分を除去して小文字化し、先頭に言語コード（Swiftの場合はls/）を付けることで、Docset 内部の検索キー（request_key）に変換できる。</p>

<table>
  <thead>
    <tr>
      <th>doc://識別子の例</th>
      <th>変換後のrequest_key (Swift)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>doc://com.apple.SwiftUI/documentation/SwiftUI/Landmarks-Building-an-app-with-Liquid-Glass</td>
      <td>ls/documentation/swiftui/landmarks-building-an-app-with-liquid-glass</td>
    </tr>
    <tr>
      <td>doc://com.apple.documentation/documentation/TechnologyOverviews/adopting-liquid-glass</td>
      <td>ls/documentation/technologyoverviews/adopting-liquid-glass</td>
    </tr>
    <tr>
      <td>doc://com.apple.documentation/tutorials/swiftui-concepts</td>
      <td>ls/tutorials/swiftui-concepts</td>
    </tr>
  </tbody>
</table>

<p>同様なJSONは次のコマンドでも得られる</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="s2">"https://developer.apple.com/tutorials/data/documentation/swiftui.json"</span> <span class="nt">-o</span> swiftui_doc.json
</code></pre></div></div>

<h3 id="docc-jsonからhtmlへ">DocC JSONからHTMLへ</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#### Python 変換スクリプト例 (render_docc.py)
</span><span class="kn">import</span> <span class="n">html</span>
<span class="kn">import</span> <span class="n">json</span>
<span class="kn">import</span> <span class="n">sys</span>

<span class="k">def</span> <span class="nf">docc_json_to_html</span><span class="p">(</span><span class="n">json_path</span><span class="p">,</span> <span class="n">output_html_path</span><span class="p">):</span>
  <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">json_path</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="nf">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>

  <span class="c1"># タイトル・ロール
</span>  <span class="n">title</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">metadata</span><span class="sh">"</span><span class="p">,</span> <span class="p">{}).</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">title</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">SwiftUI</span><span class="sh">"</span><span class="p">)</span>
  <span class="n">role</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">metadata</span><span class="sh">"</span><span class="p">,</span> <span class="p">{}).</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">roleHeading</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Framework</span><span class="sh">"</span><span class="p">)</span>

  <span class="c1"># 概要 (Abstract) のテキスト抽出
</span>  <span class="n">abstract_parts</span> <span class="o">=</span> <span class="p">[]</span>
  <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">data</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">abstract</span><span class="sh">"</span><span class="p">,</span> <span class="p">[]):</span>
    <span class="k">if</span> <span class="n">item</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">type</span><span class="sh">"</span><span class="p">)</span> <span class="o">==</span> <span class="sh">"</span><span class="s">text</span><span class="sh">"</span><span class="p">:</span>
      <span class="n">abstract_parts</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">text</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">)))</span>
    <span class="k">elif</span> <span class="n">item</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">type</span><span class="sh">"</span><span class="p">)</span> <span class="o">==</span> <span class="sh">"</span><span class="s">codeVoice</span><span class="sh">"</span><span class="p">:</span>
      <span class="n">abstract_parts</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">&lt;code&gt;</span><span class="si">{</span><span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">code</span><span class="sh">'</span><span class="p">,</span> <span class="sh">''</span><span class="p">))</span><span class="si">}</span><span class="s">&lt;/code&gt;</span><span class="sh">"</span><span class="p">)</span>
  <span class="n">abstract_html</span> <span class="o">=</span> <span class="sh">""</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">abstract_parts</span><span class="p">)</span>

  <span class="c1"># トピックセクション (topicSections) の抽出
</span>  <span class="n">topics_html</span> <span class="o">=</span> <span class="p">[]</span>
  <span class="k">for</span> <span class="n">section</span> <span class="ow">in</span> <span class="n">data</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">topicSections</span><span class="sh">"</span><span class="p">,</span> <span class="p">[]):</span>
    <span class="n">sec_title</span> <span class="o">=</span> <span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">section</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">title</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">))</span>
    <span class="n">sec_anchor</span> <span class="o">=</span> <span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">section</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">anchor</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">))</span>
    <span class="n">items_html</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">ident</span> <span class="ow">in</span> <span class="n">section</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">identifiers</span><span class="sh">"</span><span class="p">,</span> <span class="p">[]):</span>
      <span class="c1"># "doc://com.apple.SwiftUI/documentation/SwiftUI/Views" -&gt; "Views"
</span>      <span class="n">name</span> <span class="o">=</span> <span class="n">ident</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">"</span><span class="s">/</span><span class="sh">"</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
      <span class="n">items_html</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span>
          <span class="sa">f</span><span class="sh">"</span><span class="s">&lt;li&gt;&lt;code&gt;</span><span class="si">{</span><span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="si">}</span><span class="s">&lt;/code&gt; &lt;small style=</span><span class="sh">'</span><span class="s">color:#888</span><span class="sh">'</span><span class="s">&gt;(</span><span class="si">{</span><span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">ident</span><span class="p">)</span><span class="si">}</span><span class="s">)&lt;/small&gt;&lt;/li&gt;</span><span class="sh">"</span>
        <span class="p">)</span>

      <span class="n">topics_html</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sa">f</span><span class="sh">"""</span><span class="s">
          &lt;div class=</span><span class="sh">"</span><span class="s">section</span><span class="sh">"</span><span class="s">&gt;
              &lt;h2 id=</span><span class="sh">"</span><span class="si">{</span><span class="n">sec_anchor</span><span class="si">}</span><span class="sh">"</span><span class="s">&gt;</span><span class="si">{</span><span class="n">sec_title</span><span class="si">}</span><span class="s">&lt;/h2&gt;
              &lt;ul&gt;</span><span class="si">{</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">items_html</span><span class="p">)</span><span class="si">}</span><span class="s">&lt;/ul&gt;
          &lt;/div&gt;
          </span><span class="sh">"""</span><span class="p">)</span>

    <span class="c1"># HTML テンプレートの組み立て
</span>  <span class="n">html_content</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"""</span><span class="s">&lt;!DOCTYPE html&gt;
  &lt;html lang=</span><span class="sh">"</span><span class="s">ja</span><span class="sh">"</span><span class="s">&gt;
  &lt;head&gt;
      &lt;meta charset=</span><span class="sh">"</span><span class="s">UTF-8</span><span class="sh">"</span><span class="s">&gt;
      &lt;title&gt;</span><span class="si">{</span><span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">title</span><span class="p">)</span><span class="si">}</span><span class="s"> - Apple Reference&lt;/title&gt;
      &lt;style&gt;
        body </span><span class="si">{</span> <span class="n">font</span><span class="o">-</span><span class="n">family</span><span class="si">:</span> <span class="o">-</span><span class="n">apple</span><span class="o">-</span><span class="n">system</span><span class="p">,</span> <span class="n">BlinkMacSystemFont</span><span class="p">,</span> <span class="sh">"</span><span class="s">Segoe UI</span><span class="sh">"</span><span class="p">,</span>
        <span class="n">Roboto</span><span class="p">,</span> <span class="n">Helvetica</span><span class="p">,</span> <span class="n">Arial</span><span class="p">,</span> <span class="n">sans</span><span class="o">-</span><span class="n">serif</span><span class="p">;</span> <span class="nb">max</span><span class="o">-</span><span class="n">width</span><span class="si">:</span> <span class="mi">860</span><span class="n">px</span><span class="p">;</span> <span class="n">margin</span><span class="si">:</span> <span class="mi">40</span><span class="n">px</span> <span class="n">auto</span><span class="p">;</span> <span class="n">padding</span><span class="si">:</span> <span class="mi">0</span> <span class="mi">20</span><span class="n">px</span><span class="p">;</span> <span class="n">line</span><span class="o">-</span><span class="n">height</span><span class="si">:</span> <span class="mf">1.6</span><span class="p">;</span> <span class="n">color</span><span class="si">:</span> <span class="c1">#1d1d1f; </span><span class="si">}</span><span class="s">
        .badge </span><span class="si">{</span> <span class="n">background</span><span class="si">:</span> <span class="c1">#f5f5f7; border</span><span class="si">:</span> <span class="mi">1</span><span class="n">px</span> <span class="n">solid</span> <span class="c1">#d2d2d7; padding</span><span class="si">:</span> <span class="mi">2</span><span class="n">px</span> <span class="mi">8</span><span class="n">px</span><span class="p">;</span> <span class="n">border</span><span class="o">-</span><span class="n">radius</span><span class="si">:</span> <span class="mi">4</span><span class="n">px</span><span class="p">;</span> <span class="n">font</span><span class="o">-</span><span class="n">size</span><span class="si">:</span> <span class="mf">0.85</span><span class="n">em</span><span class="p">;</span> <span class="n">color</span><span class="si">:</span> <span class="c1">#86868b; text-transform</span><span class="si">:</span>
        <span class="n">uppercase</span><span class="p">;</span> <span class="si">}</span><span class="s">
        h1 </span><span class="si">{</span> <span class="n">margin</span><span class="o">-</span><span class="n">top</span><span class="si">:</span> <span class="mi">8</span><span class="n">px</span><span class="p">;</span> <span class="n">font</span><span class="o">-</span><span class="n">size</span><span class="si">:</span> <span class="mf">2.4</span><span class="n">em</span><span class="p">;</span> <span class="si">}</span><span class="s">
        .abstract </span><span class="si">{</span> <span class="n">font</span><span class="o">-</span><span class="n">size</span><span class="si">:</span> <span class="mf">1.2</span><span class="n">em</span><span class="p">;</span> <span class="n">color</span><span class="si">:</span> <span class="c1">#515154; margin</span><span class="si">:</span> <span class="mi">20</span><span class="n">px</span> <span class="mi">0</span> <span class="mi">40</span><span class="n">px</span> <span class="mi">0</span><span class="p">;</span> <span class="si">}</span><span class="s">
        .section </span><span class="si">{</span> <span class="n">margin</span><span class="o">-</span><span class="n">top</span><span class="si">:</span> <span class="mi">32</span><span class="n">px</span><span class="p">;</span> <span class="n">border</span><span class="o">-</span><span class="n">top</span><span class="si">:</span> <span class="mi">1</span><span class="n">px</span> <span class="n">solid</span> <span class="c1">#e5e5e7; padding-top</span><span class="si">:</span> <span class="mi">16</span><span class="n">px</span><span class="p">;</span> <span class="si">}</span><span class="s">
        h2 </span><span class="si">{</span> <span class="n">font</span><span class="o">-</span><span class="n">size</span><span class="si">:</span> <span class="mf">1.4</span><span class="n">em</span><span class="p">;</span> <span class="n">color</span><span class="si">:</span> <span class="c1">#1d1d1f; </span><span class="si">}</span><span class="s">
        ul </span><span class="si">{</span> <span class="nb">list</span><span class="o">-</span><span class="n">style</span><span class="o">-</span><span class="nb">type</span><span class="si">:</span> <span class="n">none</span><span class="p">;</span> <span class="n">padding</span><span class="o">-</span><span class="n">left</span><span class="si">:</span> <span class="mi">0</span><span class="p">;</span> <span class="si">}</span><span class="s">
        li </span><span class="si">{</span> <span class="n">padding</span><span class="si">:</span> <span class="mi">6</span><span class="n">px</span> <span class="mi">0</span><span class="p">;</span> <span class="n">border</span><span class="o">-</span><span class="n">bottom</span><span class="si">:</span> <span class="mi">1</span><span class="n">px</span> <span class="n">solid</span> <span class="c1">#f5f5f7; </span><span class="si">}</span><span class="s">
        code </span><span class="si">{</span> <span class="n">background</span><span class="si">:</span> <span class="c1">#f5f5f7; padding</span><span class="si">:</span> <span class="mi">2</span><span class="n">px</span> <span class="mi">6</span><span class="n">px</span><span class="p">;</span> <span class="n">border</span><span class="o">-</span><span class="n">radius</span><span class="si">:</span> <span class="mi">4</span><span class="n">px</span><span class="p">;</span> <span class="n">font</span><span class="o">-</span><span class="n">family</span><span class="si">:</span> <span class="n">ui</span><span class="o">-</span><span class="n">monospace</span><span class="p">,</span> <span class="n">Menlo</span><span class="p">,</span> <span class="n">Monaco</span><span class="p">,</span> <span class="n">monospace</span><span class="p">;</span> <span class="n">color</span><span class="si">:</span> <span class="c1">#bf5af2; </span><span class="si">}</span><span class="s">
      &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
      &lt;span class=</span><span class="sh">"</span><span class="s">badge</span><span class="sh">"</span><span class="s">&gt;</span><span class="si">{</span><span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">role</span><span class="p">)</span><span class="si">}</span><span class="s">&lt;/span&gt;
      &lt;h1&gt;</span><span class="si">{</span><span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">title</span><span class="p">)</span><span class="si">}</span><span class="s">&lt;/h1&gt;
      &lt;div class=</span><span class="sh">"</span><span class="s">abstract</span><span class="sh">"</span><span class="s">&gt;</span><span class="si">{</span><span class="n">abstract_html</span><span class="si">}</span><span class="s">&lt;/div&gt;
      </span><span class="si">{</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">topics_html</span><span class="p">)</span><span class="si">}</span><span class="s">
  &lt;/body&gt;
  &lt;/html&gt;</span><span class="sh">"""</span>

  <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">output_html_path</span><span class="p">,</span> <span class="sh">"</span><span class="s">w</span><span class="sh">"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">html_content</span><span class="p">)</span>

  <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">HTML を生成しました: </span><span class="si">{</span><span class="n">output_html_path</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
  <span class="nf">docc_json_to_html</span><span class="p">(</span><span class="sh">"</span><span class="s">39_p.json</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">swiftui.html</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/2026/dash_docset.png" alt="dash_docset" style="zoom:33%;" /></p>]]></content><author><name>Tana Gone</name></author><category term="SwiftUI" /><summary type="html"><![CDATA[オフラインで動作するドキュメントViewerアプリDashが表示するコンテンツは、brotli圧縮されたアップルのDocC JSONに格納されている。検索キー(e.g. SwiftUI Framework)からコンテンツの取り出し方を探ってみた。]]></summary></entry><entry><title type="html">Observable macro</title><link href="/swiftui/observable-macro/" rel="alternate" type="text/html" title="Observable macro" /><published>2026-07-29T17:59:28+09:00</published><updated>2026-07-29T17:59:28+09:00</updated><id>/swiftui/observable-macro</id><content type="html" xml:base="/swiftui/observable-macro/"><![CDATA[<p>macOS14(2024)以降Observableマクロが使える。2026時点で導入から3年目を迎える。移行ガイドはXcode Documentationをsample code: Observableとすればヒットするのだが、Xcodeを起動せずとも移行の要点を把握できる様に、移行ガイド記載のコードの断片をここに記載する。
<br />
<a href="https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro">Migrating from the Observable Object protocol to the Observable macro: Apple Developer Documentation</a></p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// BEFORE</span>
<span class="kd">import</span> <span class="kt">SwiftUI</span>
<span class="kd">class</span> <span class="kt">Library</span><span class="p">:</span> <span class="kt">ObservableObject</span> <span class="p">{</span>
    <span class="kd">@Published</span> <span class="k">var</span> <span class="nv">books</span><span class="p">:</span> <span class="p">[</span><span class="kt">Book</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Book</span><span class="p">(),</span> <span class="kt">Book</span><span class="p">(),</span> <span class="kt">Book</span><span class="p">()]</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">Book</span><span class="p">:</span> <span class="kt">ObservableObject</span><span class="p">,</span> <span class="kt">Identifiable</span> <span class="p">{</span>
    <span class="kd">@Published</span> <span class="k">var</span> <span class="nv">title</span> <span class="o">=</span> <span class="s">"Sample Book Title"</span>
    <span class="k">let</span> <span class="nv">id</span> <span class="o">=</span> <span class="kt">UUID</span><span class="p">()</span> <span class="c1">// A unique identifier that never changes.</span>
<span class="p">}</span>
</code></pre></div></div>

<p>note: Xcode Documentationでは2つ目のLibraryにはObservableObjectプロトコルへの準拠の記載が無いが誤記だと思われる。</p>

<p>SwiftUIに監視させるにはその型(class)にマクロを書く</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// AFTER</span>
<span class="kd">import</span> <span class="kt">SwiftUI</span>
<span class="kd">@Observable</span> <span class="kd">class</span> <span class="kt">Library</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">books</span><span class="p">:</span> <span class="p">[</span><span class="kt">Book</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Book</span><span class="p">(),</span> <span class="kt">Book</span><span class="p">(),</span> <span class="kt">Book</span><span class="p">()]</span>
<span class="p">}</span>

<span class="kd">@Observable</span> <span class="kd">class</span> <span class="kt">Book</span><span class="p">:</span> <span class="kt">Identifiable</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">title</span> <span class="o">=</span> <span class="s">"Sample Book Title"</span>    
    <span class="k">let</span> <span class="nv">id</span> <span class="o">=</span> <span class="kt">UUID</span><span class="p">()</span> <span class="c1">// A unique identifier that never changes.</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Appを構成するWindow全体で値を共有したい場合、＠StateObjectプロパティラッパーを使用し、Windowへの注入にはenvironmentObjectモディファイアを使用</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// BEFORE</span>
<span class="kd">@main</span>
<span class="kd">struct</span> <span class="kt">BookReaderApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">library</span> <span class="o">=</span> <span class="kt">Library</span><span class="p">()</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">Scene</span> <span class="p">{</span>
        <span class="kt">WindowGroup</span> <span class="p">{</span>
            <span class="kt">LibraryView</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">environmentObject</span><span class="p">(</span><span class="n">library</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>あれこれ異なる</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// AFTER</span>
<span class="kd">@main</span>
<span class="kd">struct</span> <span class="kt">BookReaderApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">library</span> <span class="o">=</span> <span class="kt">Library</span><span class="p">()</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">Scene</span> <span class="p">{</span>
        <span class="kt">WindowGroup</span> <span class="p">{</span>
            <span class="kt">LibraryView</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">environment</span><span class="p">(</span><span class="n">library</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Windowに注入された変数の受け取り</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// BEFORE</span>
<span class="kd">struct</span> <span class="kt">LibraryView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@EnvironmentObject</span> <span class="k">var</span> <span class="nv">library</span><span class="p">:</span> <span class="kt">Library</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">List</span><span class="p">(</span><span class="n">library</span><span class="o">.</span><span class="n">books</span><span class="p">)</span> <span class="p">{</span> <span class="n">book</span> <span class="k">in</span>
            <span class="kt">BookView</span><span class="p">(</span><span class="nv">book</span><span class="p">:</span> <span class="n">book</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>あれこれ異なる</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// AFTER</span>
<span class="kd">struct</span> <span class="kt">LibraryView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Environment</span><span class="p">(</span><span class="kt">Library</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">library</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">List</span><span class="p">(</span><span class="n">library</span><span class="o">.</span><span class="n">books</span><span class="p">)</span> <span class="p">{</span> <span class="n">book</span> <span class="k">in</span>
            <span class="kt">BookView</span><span class="p">(</span><span class="nv">book</span><span class="p">:</span> <span class="n">book</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>監視されたオブジェクトをWindowで利用。book変数はBookView生成時に引数に与える</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// BEFORE</span>
<span class="kd">struct</span> <span class="kt">BookView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@ObservedObject</span> <span class="k">var</span> <span class="nv">book</span><span class="p">:</span> <span class="kt">Book</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">isEditorPresented</span> <span class="o">=</span> <span class="kc">false</span>
    
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">HStack</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
            <span class="kt">Spacer</span><span class="p">()</span>
            <span class="kt">Button</span><span class="p">(</span><span class="s">"Edit"</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">isEditorPresented</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">sheet</span><span class="p">(</span><span class="nv">isPresented</span><span class="p">:</span> <span class="n">$isEditorPresented</span><span class="p">)</span> <span class="p">{</span>
            <span class="kt">BookEditView</span><span class="p">(</span><span class="nv">book</span><span class="p">:</span> <span class="n">book</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>@ObservedObjectプロパティラッパーは不要</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// AFTER</span>
<span class="kd">struct</span> <span class="kt">BookView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">book</span><span class="p">:</span> <span class="kt">Book</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">isEditorPresented</span> <span class="o">=</span> <span class="kc">false</span>
    
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">HStack</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
            <span class="kt">Spacer</span><span class="p">()</span>
            <span class="kt">Button</span><span class="p">(</span><span class="s">"Edit"</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">isEditorPresented</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">sheet</span><span class="p">(</span><span class="nv">isPresented</span><span class="p">:</span> <span class="n">$isEditorPresented</span><span class="p">)</span> <span class="p">{</span>
            <span class="kt">BookEditView</span><span class="p">(</span><span class="nv">book</span><span class="p">:</span> <span class="n">book</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>上記コードのBookEditView生成時に引数で与えられたbook変数にはプロパティラッパーが必要</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// BEFORE</span>
<span class="kd">struct</span> <span class="kt">BookEditView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@ObservedObject</span> <span class="k">var</span> <span class="nv">book</span><span class="p">:</span> <span class="kt">Book</span>
    <span class="kd">@Environment</span><span class="p">(\</span><span class="o">.</span><span class="n">dismiss</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">dismiss</span>
    
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span><span class="p">()</span> <span class="p">{</span>
            <span class="kt">TextField</span><span class="p">(</span><span class="s">"Title"</span><span class="p">,</span> <span class="nv">text</span><span class="p">:</span> <span class="n">$book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">textFieldStyle</span><span class="p">(</span><span class="o">.</span><span class="n">roundedBorder</span><span class="p">)</span>
                <span class="o">.</span><span class="n">onSubmit</span> <span class="p">{</span>
                    <span class="nf">dismiss</span><span class="p">()</span>
                <span class="p">}</span>
                
            <span class="kt">Button</span><span class="p">(</span><span class="s">"Close"</span><span class="p">)</span> <span class="p">{</span>
                <span class="nf">dismiss</span><span class="p">()</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">buttonStyle</span><span class="p">(</span><span class="o">.</span><span class="n">borderedProminent</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>子Windowであれば@Bindableプロパティラッパーを使う</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// AFTER</span>
<span class="kd">struct</span> <span class="kt">BookEditView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Bindable</span> <span class="k">var</span> <span class="nv">book</span><span class="p">:</span> <span class="kt">Book</span>
    <span class="kd">@Environment</span><span class="p">(\</span><span class="o">.</span><span class="n">dismiss</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">dismiss</span>
    
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span><span class="p">()</span> <span class="p">{</span>
            <span class="kt">TextField</span><span class="p">(</span><span class="s">"Title"</span><span class="p">,</span> <span class="nv">text</span><span class="p">:</span> <span class="n">$book</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">textFieldStyle</span><span class="p">(</span><span class="o">.</span><span class="n">roundedBorder</span><span class="p">)</span>
                <span class="o">.</span><span class="n">onSubmit</span> <span class="p">{</span>
                    <span class="nf">dismiss</span><span class="p">()</span>
                <span class="p">}</span>
                
            <span class="kt">Button</span><span class="p">(</span><span class="s">"Close"</span><span class="p">)</span> <span class="p">{</span>
                <span class="nf">dismiss</span><span class="p">()</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">buttonStyle</span><span class="p">(</span><span class="o">.</span><span class="n">borderedProminent</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Tana Gone</name></author><category term="SwiftUI" /><summary type="html"><![CDATA[macOS14(2024)以降Observableマクロが使える。2026時点で導入から3年目を迎える。移行ガイドはXcode Documentationをsample code: Observableとすればヒットするのだが、Xcodeを起動せずとも移行の要点を把握できる様に、移行ガイド記載のコードの断片をここに記載する。 Migrating from the Observable Object protocol to the Observable macro: Apple Developer Documentation]]></summary></entry><entry><title type="html">Rubyの正規表現とUnicodeプロパティ</title><link href="/ruby/rubyunicode/" rel="alternate" type="text/html" title="Rubyの正規表現とUnicodeプロパティ" /><published>2026-07-19T20:41:00+09:00</published><updated>2026-07-19T20:41:00+09:00</updated><id>/ruby/rubyunicode</id><content type="html" xml:base="/ruby/rubyunicode/"><![CDATA[<p>Rubyの正規表現は鬼雲(Onigmo)C言語ライブラリで実現されており最新版は2019-01リリースのものである。鬼雲は鬼車からフォークされいる。鬼車はフォーク後も開発が2025年まで継続されるも現在では開発中止が宣言されている。
OnigumoではUnicodeプロパティ(Unicode character property)を使うことが出来る。どういうことかというと、このプロパティLetterを使えば/\p{Letter}+/といったパターンマッチが可能なのだ。
note: Ruby3.xは2020 ‐ 2024年に年に1回リリースされた。xの部分は西暦の1桁目と一致。</p>

<p>このプロパティのリストはOnigumoのGitHub Repoにありdoc/UnicodeProps.txtがそれである。この文書を見ると次のカテゴリーに分類されている。プロパティにはどの様なものがあり各々どの様なコードポイントが含まれるのかを知りたいところだが、これが容易ではない。</p>

<ul>
  <li>POSIX brackets</li>
  <li>Special</li>
  <li>Major and General Categories</li>
  <li>Scripts</li>
  <li>DerivedCoreProperties</li>
  <li>PropList</li>
  <li>Emoji</li>
  <li>PropertyAliases</li>
  <li>PropertyValueAliases (General_Category)</li>
  <li>PropertyValueAliases (Script)</li>
  <li>DerivedAges</li>
  <li>Blocks</li>
</ul>

<p>Wikipedia.enでUnicode character propertyを検索すると付属文書#44に詳細があるのだが、#44を起点に更に辿って行かないと詳細には辿り着かない。</p>

<p>そこで試行錯誤によりこのプロパティを使ってみる。</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span> <span class="o">=</span> <span class="s2">"漢字あaＡＢｃ123パターンＤＥＦ１２３"</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/\p{Letter}+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"漢字あaＡＢｃ"</span><span class="p">,</span> <span class="s2">"パターンＤＥＦ"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/\p{In_Halfwidth_and_Fullwidth_Forms}+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"ＡＢｃ"</span><span class="p">,</span> <span class="s2">"ＤＥＦ１２３"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Letter}\p{In_Halfwidth_and_Fullwidth_Forms}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"漢字あaＡＢｃ"</span><span class="p">,</span> <span class="s2">"パターンＤＥＦ１２３"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Han}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"漢字"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{In_Katakana}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"パターン"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Katakana}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"パタ"</span><span class="p">,</span> <span class="s2">"ン"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Nd}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"123"</span><span class="p">,</span> <span class="s2">"１２３"</span><span class="p">]</span>
</code></pre></div></div>

<p>役物</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span> <span class="o">=</span> <span class="s2">"グリーンエナジー＆カンパニー"</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Po}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"＆"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Lo}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"グリ"</span><span class="p">,</span> <span class="s2">"ンエナジ"</span><span class="p">,</span> <span class="s2">"カンパニ"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Common}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"ー"</span><span class="p">,</span> <span class="s2">"ー＆"</span><span class="p">,</span> <span class="s2">"ー"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Common}\p{Letter}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"グリーンエナジー＆カンパニー"</span><span class="p">]</span>

</code></pre></div></div>

<p>役物２</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span> <span class="o">=</span> <span class="s2">"Ｌｉｂ　Ｗｏｒｋ"</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Common}\p{Letter}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"Ｌｉｂ　Ｗｏｒｋ"</span><span class="p">]</span>
</code></pre></div></div>

<p>全角スペース
Commonは便利なのだが半角スペースで区切られた２つの会社名を含む文字列から会社名を取り出せない。全角スペース\u3000を使わないと全角スペースを含む会社名も取り出せない。役物を含む会社名はPo = Punctuation Otherプロパティを使えば取り出せる。</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span> <span class="o">=</span> <span class="s2">"Ｌｉｂ　Ｗｏｒｋ 富士ピー・エス グリーンエナジー＆カンパニー"</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Po}\p{Letter}\u3000]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"Ｌｉｂ　Ｗｏｒｋ"</span><span class="p">,</span> <span class="s2">"富士ピー・エス"</span><span class="p">,</span> <span class="s2">"グリーンエナジー＆カンパニー"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Po}\p{Letter}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"Ｌｉｂ"</span><span class="p">,</span> <span class="s2">"Ｗｏｒｋ"</span><span class="p">,</span> <span class="s2">"富士ピー・エス"</span><span class="p">,</span> <span class="s2">"グリーンエナジー＆カンパニー"</span><span class="p">]</span>

<span class="n">m</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">scan</span> <span class="sr">/[\p{Common}\p{Letter}]+/</span>
<span class="o">=&gt;</span> <span class="p">[</span><span class="s2">"Ｌｉｂ　Ｗｏｒｋ 富士ピー・エス グリーンエナジー＆カンパニー"</span><span class="p">]</span>
</code></pre></div></div>

<p>Commonプロパティに含まれる文字を紹介して下さい
<code class="language-plaintext highlighter-rouge">Common</code><strong>（スクリプト名:</strong> <code class="language-plaintext highlighter-rouge">Zyyy</code><strong>）</strong>は、特定の言語や文字体系（ラテン文字、ひらがな、漢字など）に限定されず、<strong>「複数の言語やシステムで共通して使われる文字や記号」</strong>が分類されるUnicodeプロパティです。 [<a href="https://www.regular-expressions.info/unicodescript.html">1</a>]</p>

<p>このプロパティには、私たちが日常的に目にする記号やスペース、数字の多くが含まれています。代表的な文字のカテゴリーと具体例は以下の通りです。 [<a href="https://en.wikipedia.org/wiki/Script_(Unicode)">1</a>, <a href="https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/properties">2</a>]</p>

<ol>
  <li>句読点・区切り文字 (Punctuation)</li>
</ol>

<p>文章の構造を作るための記号は、言語を問わず使われるため <code class="language-plaintext highlighter-rouge">Common</code> に分類されます。 [<a href="https://www.regular-expressions.info/unicodescript.html">1</a>]</p>

<ul>
  <li><strong>半角記号</strong>: <code class="language-plaintext highlighter-rouge">,</code>（カンマ）、<code class="language-plaintext highlighter-rouge">.</code>（ピリオド）、<code class="language-plaintext highlighter-rouge">?</code>（疑問符）、<code class="language-plaintext highlighter-rouge">!</code>（感嘆符）、<code class="language-plaintext highlighter-rouge">:</code>（コロン）、<code class="language-plaintext highlighter-rouge">;</code>（セミコロン）</li>
  <li><strong>全角記号</strong>: <code class="language-plaintext highlighter-rouge">、</code>（読点）、<code class="language-plaintext highlighter-rouge">。</code>（句点）、<code class="language-plaintext highlighter-rouge">？</code>（全角疑問符）、<code class="language-plaintext highlighter-rouge">！</code>（全角感嘆符）</li>
  <li><strong>括弧（かっこ）類</strong>: <code class="language-plaintext highlighter-rouge">()</code>、<code class="language-plaintext highlighter-rouge">[]</code>、<code class="language-plaintext highlighter-rouge">{}</code>、<code class="language-plaintext highlighter-rouge">「」</code>、<code class="language-plaintext highlighter-rouge">『』</code>、<code class="language-plaintext highlighter-rouge">【】</code>、<code class="language-plaintext highlighter-rouge">（）</code> [<a href="https://www.tnjapan.net/6030/">1</a>]</li>
</ul>

<ol>
  <li>算術演算子・数学記号 (Symbols)</li>
</ol>

<p>世界共通で使われる数式用の記号です。</p>

<ul>
  <li><strong>演算子</strong>: <code class="language-plaintext highlighter-rouge">+</code>（プラス）、<code class="language-plaintext highlighter-rouge">×</code>（乗算）、<code class="language-plaintext highlighter-rouge">÷</code>（除算）、<code class="language-plaintext highlighter-rouge">=</code>（等号）、<code class="language-plaintext highlighter-rouge">&lt;</code>（不等号）、<code class="language-plaintext highlighter-rouge">√</code>（平方根）</li>
  <li><strong>その他</strong>: <code class="language-plaintext highlighter-rouge">∞</code>（無限大）、<code class="language-plaintext highlighter-rouge">%</code>（パーセント）、<code class="language-plaintext highlighter-rouge">‰</code>（パーミル）</li>
  <li>※注意: <code class="language-plaintext highlighter-rouge">-</code>（マイナス/ハイフン）も <code class="language-plaintext highlighter-rouge">Common</code> です。</li>
</ul>

<ol>
  <li>空白文字 (Separators / Whitespace)</li>
</ol>

<p>文字と文字の間を空けるためのスペース全般です。 [<a href="https://www.regular-expressions.info/unicodescript.html">1</a>, <a href="https://devo.jp/seolaboratory/65607/">2</a>, <a href="https://atmarkit.itmedia.co.jp/ait/articles/0102/28/news001.html">3</a>]</p>

<ul>
  <li><strong>半角スペース</strong>（<code class="language-plaintext highlighter-rouge">U+0020</code>）</li>
  <li><strong>全角スペース</strong>（<code class="language-plaintext highlighter-rouge">U+3000</code>）</li>
  <li>その他、タブ文字や各種制御用の特殊なスペース</li>
</ul>

<ol>
  <li>通貨記号 (Currency Symbols)</li>
</ol>

<p>各国の通貨を表す記号の多くが該当します。</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">¥</code>（円記号）、<code class="language-plaintext highlighter-rouge">$</code>（ドル記号）、<code class="language-plaintext highlighter-rouge">€</code>（ユーロ記号）、<code class="language-plaintext highlighter-rouge">£</code>（ポンド記号）、<code class="language-plaintext highlighter-rouge">₩</code>（ウォン記号） [<a href="https://help.makeleaps.jp/ja/articles/6459629-ユーザー定義テンプレート-利用可能な変数">1</a>]</li>
</ul>

<ol>
  <li>一般的な記号・アイコン (Miscellaneous Symbols)</li>
</ol>

<ul>
  <li><strong>日常の記号</strong>: <code class="language-plaintext highlighter-rouge">&amp;</code>（アンパサンド）、<code class="language-plaintext highlighter-rouge">@</code>（アットマーク）、<code class="language-plaintext highlighter-rouge">*</code>（アスタリスク）、<code class="language-plaintext highlighter-rouge">#</code>（シャープ/ハッシュ）、<code class="language-plaintext highlighter-rouge">§</code>（セクション）</li>
  <li><strong>全角の記号</strong>: <code class="language-plaintext highlighter-rouge">＆</code>、<code class="language-plaintext highlighter-rouge">＠</code>、<code class="language-plaintext highlighter-rouge">※</code>（米印）、<code class="language-plaintext highlighter-rouge">〒</code>（郵便番号マーク）</li>
  <li><strong>矢印</strong>: <code class="language-plaintext highlighter-rouge">←</code>、<code class="language-plaintext highlighter-rouge">↑</code>、<code class="language-plaintext highlighter-rouge">→</code>、<code class="language-plaintext highlighter-rouge">↓</code></li>
  <li><strong>絵文字（Emoji）の一部</strong>: 多くの絵文字も特定の言語に属さないため <code class="language-plaintext highlighter-rouge">Common</code> に分類されます。 [<a href="https://www.regular-expressions.info/unicodescript.html">1</a>]</li>
</ul>

<ol>
  <li>アラビア数字 (Digits)</li>
</ol>

<p>世界中で使われる <code class="language-plaintext highlighter-rouge">0</code> から <code class="language-plaintext highlighter-rouge">9</code> までの数字、およびその全角文字です。</p>

<ul>
  <li><strong>半角数字</strong>: <code class="language-plaintext highlighter-rouge">0</code>, <code class="language-plaintext highlighter-rouge">1</code>, <code class="language-plaintext highlighter-rouge">2</code>, <code class="language-plaintext highlighter-rouge">3</code>, <code class="language-plaintext highlighter-rouge">4</code>, <code class="language-plaintext highlighter-rouge">5</code>, <code class="language-plaintext highlighter-rouge">6</code>, <code class="language-plaintext highlighter-rouge">7</code>, <code class="language-plaintext highlighter-rouge">8</code>, <code class="language-plaintext highlighter-rouge">9</code></li>
  <li><strong>全角数字</strong>: <code class="language-plaintext highlighter-rouge">０</code>, <code class="language-plaintext highlighter-rouge">１</code>, <code class="language-plaintext highlighter-rouge">２</code>, <code class="language-plaintext highlighter-rouge">３</code>, <code class="language-plaintext highlighter-rouge">４</code>, <code class="language-plaintext highlighter-rouge">５</code>, <code class="language-plaintext highlighter-rouge">６</code>, <code class="language-plaintext highlighter-rouge">７</code>, <code class="language-plaintext highlighter-rouge">８</code>, <code class="language-plaintext highlighter-rouge">９</code></li>
  <li>※ローマ数字（Ⅰ, Ⅱなど）も <code class="language-plaintext highlighter-rouge">Common</code> に含まれます。 [<a href="https://www.city.toyohashi.lg.jp/25630.htm">1</a>, <a href="https://www.regular-expressions.info/unicodescript.html">2</a>]</li>
</ul>]]></content><author><name>Tana Gone</name></author><category term="Ruby" /><summary type="html"><![CDATA[Rubyの正規表現は鬼雲(Onigmo)C言語ライブラリで実現されており最新版は2019-01リリースのものである。鬼雲は鬼車からフォークされいる。鬼車はフォーク後も開発が2025年まで継続されるも現在では開発中止が宣言されている。 OnigumoではUnicodeプロパティ(Unicode character property)を使うことが出来る。どういうことかというと、このプロパティLetterを使えば/\p{Letter}+/といったパターンマッチが可能なのだ。 note: Ruby3.xは2020 ‐ 2024年に年に1回リリースされた。xの部分は西暦の1桁目と一致。]]></summary></entry><entry><title type="html">SQLite3でテーブルにある既存レコードの変更</title><link href="/ruby/sqlite3-update/" rel="alternate" type="text/html" title="SQLite3でテーブルにある既存レコードの変更" /><published>2026-07-12T19:08:22+09:00</published><updated>2026-07-12T19:08:22+09:00</updated><id>/ruby/sqlite3-update</id><content type="html" xml:base="/ruby/sqlite3-update/"><![CDATA[<p>Primary Key、Unique Keyが設定されたスキーマをもつテーブルに対してレコードの変更が出来る。例えば、とある銘柄の株価4本値をテーブルに格納後にレコードを修正する場合に、日付をPrimary Keyにしておけばレコードの重複を防ぎながら変更も出来る。変更には次の3つの方法があり、微妙にユースケースが異なる</p>

<ol>
  <li>
    <p>新規追加もしくは変更</p>

    <p>2番目のレコードの変更を行ったが、nameカラムの値を指定していないのでNULLが代入されてしまっている。replaceはinsert or replaceと記載しても動作は変わらない。</p>

    <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="p">.</span><span class="n">ta</span>
<span class="n">users</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="p">.</span><span class="n">sch</span> <span class="n">users</span> 
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">users</span> <span class="p">(</span><span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="n">name</span> <span class="nb">TEXT</span><span class="p">,</span> <span class="n">age</span> <span class="nb">Integer</span>
<span class="p">);</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">users</span> <span class="p">;</span>
<span class="mi">1</span><span class="o">|</span><span class="err">太郎</span><span class="o">|</span>
<span class="mi">2</span><span class="o">|</span><span class="err">次郎</span><span class="o">|</span><span class="mi">40</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">replace</span> <span class="k">into</span> <span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">age</span><span class="p">)</span> <span class="k">values</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">users</span> <span class="p">;</span>
<span class="mi">1</span><span class="o">|</span><span class="err">太郎</span><span class="o">|</span>
<span class="mi">2</span><span class="o">||</span><span class="mi">30</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>NULLが代入されない様にする</p>

    <p>2番目のレコードのageカラムの値を変更し、nameカラムの値を変更したくない場合には次の様にすれば良い。下記のexcluded.ageは外部ageのことでテーブル外のinsert分のvalues句で与えた値のことである。</p>

    <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">users</span> <span class="p">;</span>
<span class="mi">1</span><span class="o">|</span><span class="err">太郎</span><span class="o">|</span>
<span class="mi">2</span><span class="o">|</span><span class="err">次郎</span><span class="o">|</span><span class="mi">30</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">insert</span> <span class="k">into</span> <span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">age</span><span class="p">)</span> <span class="k">values</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span> <span class="k">on</span> <span class="n">conflict</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="k">do</span> <span class="k">update</span> <span class="k">SET</span> <span class="n">age</span> <span class="o">=</span> <span class="n">EXCLUDED</span><span class="p">.</span><span class="n">age</span><span class="p">;</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">users</span> <span class="p">;</span>
<span class="mi">1</span><span class="o">|</span><span class="err">太郎</span><span class="o">|</span>
<span class="mi">2</span><span class="o">|</span><span class="err">次郎</span><span class="o">|</span><span class="mi">40</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>updateは既存のレコードが確実にある場合のみ実行される</p>

    <p>2番目のレコードが修正される。2番目のレコードが無いのならテーブルの変更は行われない。</p>

    <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">update</span> <span class="n">users</span> <span class="k">SET</span> <span class="n">age</span> <span class="o">=</span> <span class="mi">30</span> <span class="k">where</span> <span class="n">id</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span>
<span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">users</span> <span class="p">;</span>
<span class="mi">1</span><span class="o">|</span><span class="err">太郎</span><span class="o">|</span>
<span class="mi">2</span><span class="o">|</span><span class="err">次郎</span><span class="o">|</span><span class="mi">30</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>insertで既存のレコードと同じPKを持つレコードを挿入は失敗</p>

    <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">insert</span> <span class="k">into</span> <span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">age</span><span class="p">)</span> <span class="k">values</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
<span class="n">Runtime</span> <span class="n">error</span><span class="p">:</span> <span class="k">UNIQUE</span> <span class="k">constraint</span> <span class="n">failed</span><span class="p">:</span> <span class="n">users</span><span class="p">.</span><span class="n">id</span> <span class="p">(</span><span class="mi">19</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
</ol>]]></content><author><name>Tana Gone</name></author><category term="Ruby" /><summary type="html"><![CDATA[Primary Key、Unique Keyが設定されたスキーマをもつテーブルに対してレコードの変更が出来る。例えば、とある銘柄の株価4本値をテーブルに格納後にレコードを修正する場合に、日付をPrimary Keyにしておけばレコードの重複を防ぎながら変更も出来る。変更には次の3つの方法があり、微妙にユースケースが異なる]]></summary></entry><entry><title type="html">smtp client Exim4を使ってe-mailを送信</title><link href="/ruby/smtp-client-exim4e-mail/" rel="alternate" type="text/html" title="smtp client Exim4を使ってe-mailを送信" /><published>2026-06-26T18:45:02+09:00</published><updated>2026-06-26T18:45:02+09:00</updated><id>/ruby/smtp-client-exim4e-mail</id><content type="html" xml:base="/ruby/smtp-client-exim4e-mail/"><![CDATA[<p>Exim4の設定を何度か行ったが、たまにしか設定しないので毎度とまどってしまう。そこでlogファイルに反映される成功例と失敗例を記録しておく</p>

<h3 id="exim4のinstall">Exim4のInstall</h3>

<p>次のコマンドでLinux BoxへInstallするのだがarchiveの取得が失敗する場合には<code class="language-plaintext highlighter-rouge">sudo apt-get update</code>を実行する</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$sudo</span> apt <span class="nb">install </span>exim4 mailutils
E: Unable to fetch some archives, maybe run apt-get update or try with <span class="nt">--fix-missing</span>?
<span class="nv">$sudo</span> apt-get update
</code></pre></div></div>
<p><a href="https://www.debian.org/releases/stable/s390x/ch08s04.ja.html">8.4. 電子メールを使用するためのシステム設定</a></p>

<p>Install後に設定スクリプトを動かす</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>dpkg-reconfigure exim4-config
- インターネット経由（internet site）: 直接他のサーバーへメールを送信・受信します。固定IPアドレスとドメインがある場合に適しています。

- スマートホスト経由（mail sent by smarthost<span class="p">;</span> received via SMTP or fetchmail）: プロバイダのSMTPサーバーやGmailなどの外部リレーサーバー（スマートホスト）を経由してメールを送信します。サーバーから通知メールを送りたいだけの場合はこれを選びます。

- ローカル配信のみ（local delivery only）: サーバー内部（rootや各ユーザー宛て）のメールのみを処理し、外部には送信しません。
</code></pre></div></div>

<h3 id="smtp-serverとの接続">smtp serverとの接続</h3>

<p>2つのファイルの編集が必要。ファイルに記述する内容はメールアカウントとアプリパスワード(gmailの場合)である。アプリパスワードはGoogle Accountに2段階認証を設定すれば取得できる。</p>

<p><a href="https://support.google.com/accounts/answer/185833?hl=ja">アプリ パスワードでログインする - Google アカウント ヘルプ</a>
<br />
<a href="https://www.debian.org/doc/manuals/debian-reference/index.ja.html">Debian リファレンス</a></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>vi /etc/exim4/passwd.client
<span class="nb">sudo echo</span> <span class="s2">"*:foobar@gmail.com:nksgmzdfryvpfeuh"</span> <span class="o">&gt;&gt;</span> /etc/exim4/passwd.client

<span class="nv">$ </span><span class="nb">sudo </span>vi /etc/exim4/update-exim4.conf.conf
<span class="nb">sudo echo</span> <span class="s2">"dc_smarthost='smtp.gmail.com::587'"</span> <span class="o">&gt;&gt;</span> /etc/exim4/update-exim4.conf.conf
</code></pre></div></div>

<h3 id="送信スクリプト">送信スクリプト</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"hello"</span> | mail <span class="nt">-r</span> pi@sy <span class="nt">-s</span> <span class="s2">"test"</span> foobar@gmail.com <span class="c"># -rは無くてもOK</span>
</code></pre></div></div>

<p>Ruby</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">IO</span><span class="p">.</span><span class="nf">popen</span><span class="p">([</span><span class="s2">"mail"</span><span class="p">,</span> <span class="s2">"-r"</span><span class="p">,</span> <span class="s2">"pi@sy"</span><span class="p">,</span> <span class="s2">"-s"</span><span class="p">,</span> <span class="s2">"test"</span><span class="p">,</span> <span class="s2">"foobar@gmail.com"</span><span class="p">],</span> <span class="s2">"w"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">io</span><span class="o">|</span>
  <span class="n">io</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"hello"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>または</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env ruby</span>
<span class="nb">system</span><span class="p">(</span><span class="s1">'echo "hello" | mail -s "test" foobar@gmail.com'</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="trouble-shooting">Trouble Shooting</h3>

<p>/var/log/exim4/mainlogにログが記録される。成功時には直ぐにe-mailが届く。</p>

<p>成功例</p>

<pre><code class="language-log">2026-06-26 16:52:56 Warning: No server certificate defined; will use a selfsigned one.
 Suggested action: either install a certificate or change tls_advertise_hosts option
2026-06-26 16:52:56 exim 4.96 daemon started: pid=1078758, -q30m, listening for SMTP on [127.0.0.1]:25 [::1]:25
2026-06-26 16:52:58 Start queue run: pid=1078759
2026-06-26 16:52:58 End queue run: pid=1078759
2026-06-26 16:53:29 1wd1NR-004Wdq-2G &lt;= pi@sy U=pi P=local S=346
2026-06-26 16:53:31 1wd1NR-004Wdq-2G H=smtp.gmail.com [2404:6800:400b:c015::6c] TLS error on connection (recv): The TLS connection was non-properly terminated.
2026-06-26 16:53:31 1wd1NR-004Wdq-2G =&gt; foobar@gmail.com R=smarthost T=remote_smtp_smarthost H=smtp.gmail.com [2404:6800:400b:c015::6c] TFO X=TLS1.3:ECDHE_X25519__ECDSA_SECP256R1_SHA256__AES_256_GCM:256 CV=yes DN="CN=smtp.gmail.com" A=plain K C="250 2.0.0 OK 98e67ed59e1d1-37df400ba0bsm3473039a91.15 - gsmtp"
2026-06-26 16:53:31 1wd1NR-004Wdq-2G Completed
</code></pre>

<p>失敗例</p>

<pre><code class="language-log">2026-06-26 16:48:02 Warning: No server certificate defined; will use a selfsigned one.
 Suggested action: either install a certificate or change tls_advertise_hosts option
2026-06-26 16:48:02 exim 4.96 daemon started: pid=1078290, -q30m, listening for SMTP on [127.0.0.1]:25 [::1]:25
2026-06-26 16:48:04 Start queue run: pid=1078291
2026-06-26 16:48:04 End queue run: pid=1078291
2026-06-26 16:48:38 1wd1Ik-004WWJ-2r &lt;= pi@sy U=pi P=local S=344
2026-06-26 16:48:39 1wd1Ik-004WWJ-2r plain authenticator failed H=smtp.gmail.com [2404:6800:400b:c017::6d] 535-5.7.8 Username and Password not accepted. For more information, go to
535 5.7.8  https://support.google.com/mail/?p=BadCredentials d2e1a72fcca58-845c93a53d2sm693601b3a.33 - gsmtp
2026-06-26 16:48:40 1wd1Ik-004WWJ-2r login authenticator failed H=smtp.gmail.com [2404:6800:400b:c017::6d] 535-5.7.8 Username and Password not accepted. For more information, go to
535 5.7.8  https://support.google.com/mail/?p=BadCredentials d2e1a72fcca58-845c93a53d2sm693601b3a.33 - gsmtp
2026-06-26 16:48:40 1wd1Ik-004WWJ-2r H=smtp.gmail.com [2404:6800:400b:c017::6d] TLS error on connection (recv): The TLS connection was non-properly terminated.
2026-06-26 16:48:40 1wd1Ik-004WWJ-2r ** foobar@gmail.com R=smarthost T=remote_smtp_smarthost H=smtp.gmail.com [2404:6800:400b:c017::6d] X=TLS1.3:ECDHE_X25519__ECDSA_SECP256R1_SHA256__AES_256_GCM:256 CV=yes DN="CN=smtp.gmail.com": SMTP error from remote mail server after pipelined end of data: 530-5.7.0 Authentication Required. For more information, go to\n530 5.7.0  https://support.google.com/accounts/troubleshooter/2402620. d2e1a72fcca58-845c93a53d2sm693601b3a.33 - gsmtp
2026-06-26 16:48:40 1wd1Im-004WWP-0Z &lt;= &lt;&gt; R=1wd1Ik-004WWJ-2r U=Debian-exim P=local S=2021
2026-06-26 16:48:40 1wd1Ik-004WWJ-2r Completed
2026-06-26 16:48:40 1wd1Im-004WWP-0Z =&gt; pi &lt;pi@sy&gt; R=local_user T=mail_spool
2026-06-26 16:48:40 1wd1Im-004WWP-0Z Completed
/var/log/exim4/mainlog (END)
</code></pre>

<p>smtp serverにeonet webメールを使った場合smtp serverからはConnection timed outの応答が来た。</p>

<pre><code class="language-log">2026-06-26 18:17:08 1wd2gO-004XRy-1U &lt;= pi@sy U=pi P=local S=326
2026-06-26 18:18:41 1wd2d3-004XPj-2v H=kopt-eonet-smtps.xspmail.jp [2001:240:bb81:94:202:238:198:34]: SMTP timeout after initial connection: Connection timed out
2026-06-26 18:22:08 1wd2gO-004XRy-1U H=kopt-eonet-smtps.xspmail.jp [2001:240:bb81:94:202:238:198:34]: SMTP timeout after initial connection: Connection timed out
2026-06-26 18:23:42 1wd2d3-004XPj-2v H=kopt-eonet-smtps.xspmail.jp [202.238.198.34]: SMTP timeout after initial connection: Connection timed out
2026-06-26 18:23:42 1wd2d3-004XPj-2v == foobar@gmail.com R=smarthost T=remote_smtp_smarthost defer (110): Connection timed out H=kopt-eonet-smtps.xspmail.jp [202.238.198.34]: SMTP timeout after initial connection
2026-06-26 18:27:08 1wd2gO-004XRy-1U H=kopt-eonet-smtps.xspmail.jp [202.238.198.34]: Remote host closed connection in response to initial connection
2026-06-26 18:27:08 1wd2gO-004XRy-1U == foobar@gmail.com R=smarthost T=remote_smtp_smarthost defer (-18) H=kopt-eonet-smtps.xspmail.jp [202.238.198.34]: Remote host closed connection in response to initial connection
</code></pre>

<h3 id="bashスクリプトの中でrubyスクリプトを動かし終了ステータスに応じてメールを飛ばす">Bashスクリプトの中でRubyスクリプトを動かし終了ステータスに応じてメールを飛ばす</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 関数を定義</span>
codes<span class="o">()</span> <span class="o">{</span>
	<span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> <span class="nt">-eq</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then
		</span><span class="nb">echo</span> <span class="s2">"成功しました"</span>
	<span class="k">else
		</span><span class="nb">echo</span> <span class="s2">"失敗しました"</span>
		<span class="nb">echo</span> <span class="s2">"failed"</span> | mail <span class="nt">-s</span> <span class="s2">"failed @foo.rb"</span> hrtanaka@gmail.com
	<span class="k">fi</span>
<span class="o">}</span>

ruby zeroDiv.rb <span class="c"># RubyスクリプトはBashスクリプトと同じフォルダに配置</span>
<span class="c"># ruby -e 'puts 1/0'</span>
<span class="c"># 関数を呼び出す（カッコは不要）</span>
codes
ruby <span class="nt">-e</span> <span class="s1">'puts 1'</span>
codes
</code></pre></div></div>]]></content><author><name>Tana Gone</name></author><category term="Ruby" /><summary type="html"><![CDATA[Exim4の設定を何度か行ったが、たまにしか設定しないので毎度とまどってしまう。そこでlogファイルに反映される成功例と失敗例を記録しておく]]></summary></entry><entry><title type="html">Rapsberry Pi(Debian Linux 12)でCloudflare Warp(svc, cli)を使ってみる</title><link href="/ruby/cloudflare-warp/" rel="alternate" type="text/html" title="Rapsberry Pi(Debian Linux 12)でCloudflare Warp(svc, cli)を使ってみる" /><published>2026-04-23T14:14:47+09:00</published><updated>2026-04-23T14:14:47+09:00</updated><id>/ruby/cloudflare-warp</id><content type="html" xml:base="/ruby/cloudflare-warp/"><![CDATA[<p>Cloudflare WarpってのはCloudflare社のVPSをlocalhostのsocks5 Proxyに見せるためのdaemonとコマンドのセットである。</p>

<ol>
  <li>
    <p>apt-get install cloudflare-warpコマンド
コマンドの実行のためにはdeb PackageのURLを取得する必要がある。取得の方法はデベロッパーサイトに記載されてある。</p>

    <p><a href="https://developers.cloudflare.com/">Developers</a>
View all docs -&gt; Warp Client -&gt; OS clients -&gt; Linux</p>

    <p><a href="https://pkg.cloudflareclient.com/">Cloudflare WARP packages</a></p>
  </li>
  <li>
    <p>daemonが起動したことの確認とsocks5 Proxyの設定、動作確認</p>
  </li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% <span class="nb">sudo </span>systemctl status warp-svc.service
● warp-svc.service - Cloudflare Zero Trust Client Daemon
     Loaded: loaded <span class="o">(</span>/lib/systemd/system/warp-svc.service<span class="p">;</span> enabled<span class="p">;</span> preset: ena&gt;
     Active: active <span class="o">(</span>running<span class="o">)</span> since Wed 2026-04-22 10:16:25 JST<span class="p">;</span> 47s ago
   Main PID: 18151 <span class="o">(</span>warp-svc<span class="o">)</span>
      Tasks: 30 <span class="o">(</span>limit: 9259<span class="o">)</span>
        CPU: 209ms
     CGroup: /system.slice/warp-svc.service
             └─18151 /bin/warp-svc
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% warp-cli registration new
Success

% warp-cli connect
Success

% warp-cli status
Status update: Connected
Network: healthy

% warp-cli mode proxy
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% curl <span class="nt">-x</span> socks5h://127.0.0.1:40000 https://inet-ip.info/ip
104.28.xxx.xxx
</code></pre></div></div>

<ol>
  <li>
    <p>Proxyを複数作成する</p>

    <p>warp-dockerを複数起動すればProxyを複数作成できる。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">--socks5-hostname</span> 127.0.0.1:1080 https://cloudflare.com/cdn-cgi/trace
</code></pre></div>    </div>

    <p><a href="https://github.com/cmj2002/warp-docker">cmj2002/warp-docker: Run Cloudflare WARP in Docker.</a></p>

    <p><a href="https://github.com/smol-machines">smol machines</a></p>
  </li>
</ol>]]></content><author><name>Tana Gone</name></author><category term="Ruby" /><summary type="html"><![CDATA[Cloudflare WarpってのはCloudflare社のVPSをlocalhostのsocks5 Proxyに見せるためのdaemonとコマンドのセットである。]]></summary></entry><entry><title type="html">Pythonでgif animationファイルを作る2つの方法</title><link href="/python/pythongif-animation/" rel="alternate" type="text/html" title="Pythonでgif animationファイルを作る2つの方法" /><published>2026-04-12T09:49:43+09:00</published><updated>2026-04-12T09:49:43+09:00</updated><id>/python/pythongif-animation</id><content type="html" xml:base="/python/pythongif-animation/"><![CDATA[<p>強化学習でCartPole問題やGrid World問題を解く際に学習の進捗を映像で知りたい。動画なら一目で進捗が解る。</p>

<h3 id="matplotlib付属のanimationとpillowpython-image-library-ish">matplotlib付属のanimationとpillow(python image library-ish)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="n">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="n">gymnasium</span> <span class="k">as</span> <span class="n">gym</span>
<span class="kn">from</span> <span class="n">matplotlib</span> <span class="kn">import</span> <span class="n">animation</span>

<span class="k">def</span> <span class="nf">display_frames_as_gif</span><span class="p">(</span><span class="n">frames</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">
    Displays a list of frames as a gif
    </span><span class="sh">"""</span>
    <span class="n">plt</span><span class="p">.</span><span class="nf">figure</span><span class="p">(</span>
        <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="n">frames</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">/</span> <span class="mf">72.0</span><span class="p">,</span> <span class="n">frames</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">/</span> <span class="mf">72.0</span><span class="p">),</span>
        <span class="n">dpi</span><span class="o">=</span><span class="mi">72</span>
    <span class="p">)</span>
    <span class="n">patch</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="n">frames</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    <span class="n">plt</span><span class="p">.</span><span class="nf">axis</span><span class="p">(</span><span class="sh">'</span><span class="s">off</span><span class="sh">'</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">animate</span><span class="p">(</span><span class="n">i</span><span class="p">):</span>
        <span class="n">patch</span><span class="p">.</span><span class="nf">set_data</span><span class="p">(</span><span class="n">frames</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
        <span class="k">return</span> <span class="n">patch</span><span class="p">,</span>

    <span class="n">anim</span> <span class="o">=</span> <span class="n">animation</span><span class="p">.</span><span class="nc">FuncAnimation</span><span class="p">(</span><span class="n">plt</span><span class="p">.</span><span class="nf">gcf</span><span class="p">(),</span> <span class="n">animate</span><span class="p">,</span> <span class="n">frames</span><span class="o">=</span><span class="nf">len</span><span class="p">(</span><span class="n">frames</span><span class="p">),</span>
        <span class="n">interval</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>

    <span class="n">anim</span><span class="p">.</span><span class="nf">save</span><span class="p">(</span><span class="sh">"</span><span class="s">movie_cartpole.gif</span><span class="sh">"</span><span class="p">,</span> <span class="n">writer</span><span class="o">=</span><span class="sh">"</span><span class="s">pillow</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">plt</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>

<span class="c1"># CartPole をランダムに動かす
</span><span class="n">frames</span> <span class="o">=</span> <span class="p">[]</span>

<span class="n">env</span> <span class="o">=</span> <span class="n">gym</span><span class="p">.</span><span class="nf">make</span><span class="p">(</span><span class="sh">"</span><span class="s">CartPole-v1</span><span class="sh">"</span><span class="p">,</span> <span class="n">render_mode</span><span class="o">=</span><span class="sh">"</span><span class="s">rgb_array</span><span class="sh">"</span><span class="p">)</span>
<span class="n">observation</span><span class="p">,</span> <span class="n">info</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="nf">reset</span><span class="p">()</span>

<span class="k">for</span> <span class="n">step</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">200</span><span class="p">):</span>
    <span class="n">frame</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="nf">render</span><span class="p">()</span>
    <span class="n">frames</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">frame</span><span class="p">)</span>

    <span class="n">action</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="nf">choice</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
    <span class="n">observation</span><span class="p">,</span> <span class="n">reward</span><span class="p">,</span> <span class="n">terminated</span><span class="p">,</span> <span class="n">truncated</span><span class="p">,</span> <span class="n">info</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="nf">step</span><span class="p">(</span><span class="n">action</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">terminated</span> <span class="ow">or</span> <span class="n">truncated</span><span class="p">:</span>
        <span class="k">break</span>

<span class="n">env</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>

<span class="c1"># 動画を保存
</span><span class="nf">display_frames_as_gif</span><span class="p">(</span><span class="n">frames</span><span class="p">)</span>
</code></pre></div></div>

<ol>
  <li>framesには複数枚の画像に相当する数値データが格納されている</li>
  <li>plt.figureでgifファイルの縦横の大きさがセットされる</li>
  <li>plt.imshowで1枚の画像に相当するpatchが作成</li>
  <li>patch.set_dataでpatchの中身が入れ替わる</li>
  <li>animation.Funcanimationへanimate callback関数を登録すると連続画像のオブジェクトが生成</li>
  <li>anim.saveでgif動画が保存される</li>
</ol>

<div align="center"><img src="/images/2026/movie_qlearning.gif" alt="movie_qlearning" style="zoom:33%;" /></div>

<h3 id="matplotlib付属のmatplotlibpyplotとpilpython-image-library">matplotlib付属のmatplotlib.pyplotとPIL(python image library)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="n">io</span>
<span class="kn">from</span> <span class="n">PIL</span> <span class="kn">import</span> <span class="n">Image</span>

<span class="n">x_list</span> <span class="o">=</span> <span class="nf">range</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="n">y_list</span> <span class="o">=</span> <span class="p">[</span><span class="n">x_val</span><span class="o">**</span><span class="mi">2</span> <span class="k">for</span> <span class="n">x_val</span> <span class="ow">in</span> <span class="n">x_list</span><span class="p">]</span>

<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="nf">figure</span><span class="p">()</span>

<span class="n">img_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">x_val</span><span class="p">,</span> <span class="n">y_val</span> <span class="ow">in</span> <span class="nf">zip</span><span class="p">(</span><span class="n">x_list</span><span class="p">,</span> <span class="n">y_list</span><span class="p">):</span>
    <span class="n">plt</span><span class="p">.</span><span class="nf">xlim</span><span class="p">(</span><span class="n">x_list</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">x_list</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">plt</span><span class="p">.</span><span class="nf">ylim</span><span class="p">(</span><span class="nf">min</span><span class="p">(</span><span class="n">y_list</span><span class="p">),</span> <span class="nf">max</span><span class="p">(</span><span class="n">y_list</span><span class="p">))</span>
    <span class="n">plt</span><span class="p">.</span><span class="nf">scatter</span><span class="p">(</span><span class="n">x_val</span><span class="p">,</span> <span class="n">y_val</span><span class="p">,</span> <span class="n">c</span><span class="o">=</span><span class="sh">"</span><span class="s">tab:blue</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">img_bytes</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="nc">BytesIO</span><span class="p">()</span>
    <span class="n">plt</span><span class="p">.</span><span class="nf">savefig</span><span class="p">(</span><span class="n">img_bytes</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="sh">"</span><span class="s">png</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">img</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">img_bytes</span><span class="p">)</span>
    <span class="n">img_list</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
    
<span class="n">img_list</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">save</span><span class="p">(</span><span class="sh">"</span><span class="s">pillow_sample2.gif</span><span class="sh">"</span><span class="p">,</span> 
<span class="n">save_all</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">append_images</span><span class="o">=</span><span class="n">img_list</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">optimize</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">duration</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="n">loop</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<div align="center"><img src="images/2026/pillow_sample2.gif" alt="pillow_example" style="zoom:50%;" /></div>

<p><a href="https://3pysci.com/python-matplotlib-92/">【matplotlib】PIL（Pillow）とBytesIOを使ってGIFアニメーションを作成する方法 3PySci</a></p>]]></content><author><name>Tana Gone</name></author><category term="Python" /><summary type="html"><![CDATA[強化学習でCartPole問題やGrid World問題を解く際に学習の進捗を映像で知りたい。動画なら一目で進捗が解る。]]></summary></entry></feed>