OmniAICreator/Qiita-1.07M huggingface dataset:
【Rails初学者】Ruby3.4で erb2haml`がエラーになる件とその対処法（NoMethodError: undefined method  exists?）

# Ruby 3.4 で `erb2haml` がエラーになる件とその対処法（NoMethodError: undefined method `exists?`）

こんにちは。
エンジニア転職を目指している、現役の看護師です。
RailsアプリでERBをHamlに変換しようと`erb2haml`のタスクを実行したところ、Ruby3.4でエラーが発生しました。調査・対処して無事変換できたので忘れないように記録しておきます。

## 環境

- Ruby 3.4.2
- Rails 8.0.2
- erb2haml 0.1.5

## 発生したエラー

```bash
bundle exec rake haml:replace_erbs

Looking for ERB files to convert to Haml...
rake aborted!
NoMethodError: undefined method 'exists?' for module FileTest (NoMethodError)

        unless FileTest.exists?(haml_path)
                       ^^^^^^^^
Did you mean?  exist?
```
調べると、Ruby3.4では`FileTest.exists?`が 完全に削除されており、`exist?`を使う必要があるらしい。

## 原因と該当箇所の特定
以下のコマンドで該当箇所を検索
 ```bash
 grep -rn "FileTest.exists?" $(bundle show erb2haml)
```
以下、結果
```bash
.../gems/erb2haml-0.1.5/lib/erb2haml/railties/erb2haml.rake:29:        unless FileTest.exists?(haml_path)
.../gems/erb2haml-0.1.5/lib/erb2haml/railties/erb2haml.rake:59:        unless FileTest.exists?(haml_path)
```
## 修正内容
該当ファイルを開く
```bash
nano ~/.rbenv/versions/3.4.2/lib/ruby/gems/3.4.0/gems/erb2haml-0.1.5/lib/erb2haml/railties/erb2haml.rake
```
以下をそれぞれ置き換えします。
```bash
- unless FileTest.exists?(haml_path)
+ unless FileTest.exist?(haml_path)
 ```

## 再実行で成功
```bash
bundle exec rake haml:replace_erbs
```
正常にHamlに変換されたようです

## 自分用メモ
- Rubyのバージョンが上がると、標準メソッドでも容赦なく削除されるので要注意
- 今回みたいにgemが古いとエラーに直結するため、確認していきたい
- Ruby3.4ではFile.exists? / FileTest.exists?は削除されたみたい
　→代わりに File.exist? を使う（"s" なし）

gemがRubyの最新仕様に追いついてないこともあるので、バージョンアップ後はgemの中身もチェックするようにする
SNSやチャットで使える！豊富な絵文字＆顔文字を簡単コピペできる便利ツールの紹介

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4078713/3977b60f-41a9-48b4-841c-5db9c9704f03.png)


## 日常のコミュニケーションをもっと楽しく

SNSやチャットでのやりとりに、ちょっとした絵文字や顔文字を加えるだけで、伝わり方が大きく変わります。  
例えば、Twitterの投稿に「🚀 新機能リリース！」と絵文字を添えたり、LINEのメッセージで「( ͡° ͜ʖ ͡°)」のような顔文字を使うだけで、相手に親しみやすさや感情が伝わりやすくなります。

こうした多彩な絵文字や顔文字を手軽に探してコピーできるのが、[Emoji Symbols Copyのサイト](https://emojisymbolscopy.com/)です。  
サイトには27種類以上の顔文字カテゴリや、食べ物・動物・天体など多彩な絵文字が揃っており、目的に応じてすぐに使いたい記号を見つけられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4078713/baf70f99-1121-4e64-baa6-234b3bab3ce8.png)

## SNSプロフィールや投稿の装飾に最適

Instagramのプロフィール欄や投稿文に、🌸や🐱などの絵文字を入れると、見た目が華やかになり、フォロワーの目を引きやすくなります。  
「季節の絵文字」や「かわいい動物マーク」など、テーマ別に絵文字を探したい時は、[こちらの絵文字一覧ページ](https://emojisymbolscopy.com/)が便利です。

## チャットや仕事のコミュニケーションでの活用例

リモートワーク中のチャットで、タスクの進捗を伝える際に「✅ 完了」「⚠️ 注意」「🔄 進行中」といった記号を使うと、状況が一目でわかりやすくなります。  
こうした記号は、[記号コレクションのページ](https://emojisymbolscopy.com/)から簡単にコピー可能です。

また、顔文字で気持ちを表現したいときは、(づ｡◕‿‿◕｡)づ（ハグ顔）や (¬‿¬)（ウィンク顔）など、[顔文字一覧](https://emojisymbolscopy.com/all-lenny-face)から選べます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4078713/90c5f976-da18-414b-8bd3-82b129d69fa1.png)


## 使い方はとてもシンプル

サイトにアクセスし、欲しいカテゴリをクリック。  
気に入った絵文字や顔文字を見つけたら、ワンクリックでクリップボードにコピーできます。  
スマホからも使いやすい設計なので、どこでもサッと使いたい時に便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4078713/27ba8a6c-6484-4ea1-8bd6-a20fcc76a27e.png)


---

## まとめ

日々のSNS投稿やチャットでのコミュニケーションに、ちょっとした絵文字や顔文字を取り入れるだけで、メッセージの印象は大きく変わります。  
「Emoji Symbols Copy」は、豊富な種類の絵文字と顔文字をカテゴリ別に探せて、すぐにコピーできる便利なツールです。  

- SNSの投稿をもっと魅力的にしたい  
- チャットで感情や状況をわかりやすく伝えたい  
- 手軽に顔文字を使って会話を盛り上げたい  

そんな方は、ぜひ[こちらのサイト](https://emojisymbolscopy.com/)をチェックしてみてください。あなたのコミュニケーションがより楽しく、伝わりやすくなるはずです。

---

### ポイントまとめ

- [豊富な顔文字カテゴリ](https://emojisymbolscopy.com/all-lenny-face)で感情表現が自由自在  
- [テーマ別絵文字一覧](https://emojisymbolscopy.com/all-symbols)でSNSプロフィールを華やかに  
- [記号コレクション](https://emojisymbolscopy.com/all-emojis)で仕事のタスク管理も効率化
スタックチャンが最新ソースで動かない？Moddableへのパッチで解決した話

## はじめに

前回は、スタックチャンを Windows10 + WSL で書き込む内容（雑ですが）を紹介しました。

https://qiita.com/hiroyuki-kaneko/items/9f09a1e173a2724e1520

その際、スタックチャンの書き込みはできたもののうまく動かない事象に遭遇していました。
Web Serial API 経由での書き込みでも遭遇していた事象と同じです。（画面が真っ白になる）

https://qiita.com/hiroyuki-kaneko/items/5b13e54c088705947c44

今回、X（旧Twitter）で事象についてつぶやいていたところ、@stc1988 さんに偶然みつけてもらい助けてもらってｽﾀｯｸﾁｬﾝの顔を拝めるところまできました。

https://x.com/stc1988/status/1921922394727358637

その時の対応内容をまとめる（というか手順？）を残したいと思います。

## 修正内容

そのまんまの修正内容です。`import` の修正と、 `manifest` の修正です。一人では到底たどり着けない内容なのでとても感謝しています。

https://gist.github.com/stc1988/aecb8e4e0c365ec8f0d99592e44dd5cf


## 修正の適用

差分を提示いただいたので、適用は簡単です。

```
$ cd $MODDABLE
$ wget https://gist.githubusercontent.com/stc1988/aecb8e4e0c365ec8f0d99592e44dd5cf/raw/b72575b6f2772288a9526e31c54723d6136
f0861/cores3_touch.diff
--2025-05-13 00:41:15--  https://gist.githubusercontent.com/stc1988/aecb8e4e0c365ec8f0d99592e44dd5cf/raw/b72575b6f2772288a9526e31c54723d6136f0861/cores3_touch.diff
Resolving gist.githubusercontent.com (gist.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...
Connecting to gist.githubusercontent.com (gist.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1152 (1.1K) [text/plain]
Saving to: ‘cores3_touch.diff’

cores3_touch.diff                        100%[==================================================================================>]   1.12K  --.-KB/s    in 0.002s

2025-05-13 00:41:15 (464 KB/s) - ‘cores3_touch.diff’ saved [1152/1152]

$ git apply cores3_touch.diff
error: corrupt patch at line 25
```

おや。。。なにか、25行目が気に入らないようです。
差分は小さいので今回は手動で修正しました。

## 動作確認

気を取り直して、WSL側からデプロイします。（ビルドまでの流れは前回の記事を参照してください）

```
npm run deploy --target=esp32/m5stack_cores3
```

出力結果（一部
```
Writing at 0x0029bf26... (94 %)
Writing at 0x002a6120... (95 %)
Writing at 0x002acca6... (96 %)
Writing at 0x002b2570... (97 %)
Writing at 0x002b80e7... (98 %)
Writing at 0x002be4f5... (99 %)
Writing at 0x002c3e2d... (100 %)
Wrote 2846864 bytes (1696306 compressed) at 0x00010000 in 24.3 seconds (effective 936.1 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 120...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (120 compressed) at 0x00008000 in 0.0 seconds (effective 492.8 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
Done
```

書き込みが終わりました。

## 再起動

数秒待ち・・・とうとう来ました！

![GqwhKNZW8AAfZeR.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155313/29b461de-928d-48cd-a460-5715dc4fbc66.jpeg)

## まとめ

ｽﾀｯｸﾁｬﾝの画面が真っ白になるのは Moddable SDK に不具合が紛れ込んでいたことが原因だったようです。 @stc1988 さんには助けていただき助かりました。

修正方法は明確なので最終的には修正が取り込まれれば手動による修正は避けられますね。


https://github.com/Moddable-OpenSource/moddable/issues/1497

これで安心してスタックチャンで遊べるようになります。
ChatGPTのResponse APIでWeb検索をする基本形

OpenAIの新しい responses API をGoogle Apps Script（GAS）から叩いて、Web検索を自動で行わせる処理を試しました。


# Web検索を「勝手に」やってくれる


- tool_choice: 'auto' を指定するだけで、必要に応じて検索してくれる
- 車に関する質問を投げるだけで、最新の情報を反映した回答が返ってくる（例：ランドクルーザー250のリフトアップ費用など）
- GASから簡単にリクエスト可能。OpenAI APIキーがあればすぐ使える


# テストした質問

```javascript
const messages = [
  {
    role: 'system',
    content: '整備士風に「〜っす」「〜っすね」で語尾を揃えてください。車種やパーツ情報はWebで検索して回答してください。'
  },
  {
    role: 'user',
    content: 'ランクル250をリフトアップするといくらかかりますか？'
  }
];

```

# 基本形（GASの場合）

```javascript
function testCallWithMessages() {
  const messages = [
    {
      role: 'system',
      content: '整備士風に「〜っす」「〜っすね」で語尾を揃えてください。車種やパーツ情報はWebで検索して回答してください。'
    },
    {
      role: 'user',
      content: 'ランドクルーザー250の燃費は？'
    }
  ];
  const result = callOpenAIResponsesAPI(messages);
}

function callOpenAIResponsesAPI(messages) {

  const apiKey = PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY');

  const url = 'https://api.openai.com/v1/responses';
  const payload = {
    model: 'gpt-4o-mini',
    input: messages,
    tool_choice: 'auto',
    tools: [{ type: 'web_search' }],
    stream: false,
    store: true
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + apiKey
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  const response = UrlFetchApp.fetch(url, options);
  const responseText = response.getContentText();

  try {
    const parsed = JSON.parse(responseText);
    const messageItem = Array.isArray(parsed.output)
      ? parsed.output.find(item => item.type === 'message')
      : null;
    const outputText = messageItem?.content?.find(c => c.type === 'output_text')?.text
      || '❗ テキストが取れませんでした';
    Logger.log('📄 出力: %s', outputText);
    return parsed;
  } catch (e) {
    throw new Error('JSON解析失敗っす: ' + e.message);
  }
}

```

# 現状調べてわかっていること（OpenAI Response API 検証）

##  tool_choice と tools の動作仕様

- tool_choice: 'auto' → メッセージの内容によって検索するか自動判定される
- tool_choice: 'required' にすると、毎回ツールを実行する
    - tools を1つ（例: web_search）だけにすると、指定されたツールのみを強制的に実行

## roleごとの挙動検証（Web検索あり）

### 検証内容
- role: user に検索内容を入れる → 正しくWeb検索し、正しい回答
- role: assistant に検索内容を入れる → Web検索していそうだが、回答が誤っている

## systemメッセージのルール適用について

### 設定
- role: system に「回答に必ず絵文字を含める」と記載
- model:gpt-4o-mini

### 挙動
- Web検索を伴う質問 → 絵文字が出ない
- Web検索を伴わない質問 → 絵文字が正しく含まれる

### 考察
- 検索結果を重視する際、systemルールが弱まる（無視される？）

# 現状のまとめ
口調などはweb検索をしても回答で反映されるが、絵文字は反映されない。
modelを変更しても回答は変わらなかった。

絵文字を必ず入れたい場合はひと手間必要そう。
例えば
web検索専用のメソッドを用意する。
上記の検索結果をweb検索をしないメソッドtool_choice:noneに渡してrole:systemで絵文字を指定する。


これで一応できる気がしているが、必ずweb検索をしてしまう。= 検索の金額が毎回発生。

web検索をするかしないかを判断するなにかフラグがあればよいのかなと思ってしまうが見当たらない。

現状はここまで。
デザインは収斂進化する

ごろ寝の書き流し

デザインは収斂するんじゃないという話
ここでのデザインは概ねWebデザインのイメージです

収斂進化とは

> 収斂進化（しゅうれんしんか、英: convergent evolution）とは、複数の異なるグループの生物が、同様の生態的地位についたときに、系統に拘らず類似した形質を独立に獲得する現象である[1]。
> wikipedia

ウナギとタウナギが似てるとか、サメとイルカが似てるとか、違う分類の生物が環境や目的に合わせて似たような形に進化してるという。

ボタンやトグルなりの要素から大枠のレイアウト、飾りなど
デザインも時代は変わってもスタンダードなものはずっと変わらない
新しい技術や媒体が出てきても人間の認知パターンはそうそう変わらん
必要な機能、人の感覚に訴えるデザインは収束していくんでなかろうかと

それが収斂進化っぽいのかは書いててなんか違う気がしてきたけど
厳密に合わせようとすると頭が足りないんで
いろんな種類や奇をてらったり革新的と思われるデザインが出てきたとしても、結局はスタンダードに収束していくんでないかという、そんなイメージ
もちろん新しい発想や発見から新しいデザインが生まれることは全然あると思います

機能美に収束していくんじゃないかな
デザインは表面だけではないので
機能や目的を達成するために必要な形態や表現をするわけですよね
全体をクリアした上で装飾、あしらいといったものが生きてくるんじゃなかろうか
装飾もなぜ必要なのか？なぜ効果的なのか？という役割を説明できねばならんわけですね
そのプロジェクトの勘所を抑えてデザインする
結果的にデザインは収斂していく
答えは何パターンも出てくるわけじゃないのだろうと

パターンが出るかどうかは目的や制作者の引き出しによってだいぶ変わるんでしょうが
王道やよくある形に落ち着くことは決して悪くないと思うわけです

機能美についてはバウハウスとか 効果的な手法などは認知心理学とかアフォーダンスとかあったりするようですね

まぁなんていうか
無理して奇をてらわなくてもいいんじゃないの
長く広く利用されている手法にはそれなりの理由があるよ
なんでそのデザインはそのかたちを取ってるのか、なぜそこにあるのか、ないのか
考えながらデザインしたいものですね
でした
Geminiでベースを使う魔法少女を描いてみた。

# 概要

Geminiでベースを使う魔法少女を描いてみました。

# 実装

プロトコルと実行結果は以下の通りでした。

```
光のベースを使う魔法少女を描いてください。
```

![光のベースを使う魔法少女_20250513.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/87a40486-ccc9-4ebc-a73a-df32bda15a96.png)



```
闇のベースを使う魔法少女を描いてください。
```

![闇のベースを使う魔法少女_20250513.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/15e9d3b0-b5f3-49b0-a037-b4ce2f401363.png)


```
聖なるベースを使う魔法少女を描いてください。
```

![聖なるベースを使う魔法少女_20250513.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/260b08c2-834c-411b-b585-0175f8d95d4c.png)


# まとめ

闇の子は可愛い。
何かの役に立てばと。
PowerShellからGoogleDrive簡単にcdする方法

# 概要
Googleドライブのパソコン版をインストールしている場合、PowerShellからGoogleドライブにcdすることができます。その時のコマンドがこちら
```
cd :G/マイドライブ
```
入力方法を日本語にして、さらに英語に戻さないといけません。めんどくさいですね。簡単にできるようにしましょう。
# 結論
かなり羽陽曲折を経たのですが、結論だけ書いておきます。もっとシンプルな方法もありそうですし、環境によってはうまくいかないかもしれませんが、悪しからず。
1. ショートカットを作る
好きな場所にショートカットを作ります。ショートカット先もgoogleドライブ内の好きな場所にしてください。PowerShellに入ってすぐのディレクトにしておくのがタイピング少な目でおすすめです。
2. 関数を作る
ご存じの通りPowerShellのcdではショートカット先のディレクトリに移動することができません。そこで、以下の記事を参考にショートカット先のディレクトリへの移動に対応したcdを作成します。

https://qiita.com/BOACK/items/3e425f9dd90499c7ed18

コードはこちらです。
```
filter Get-ShortcutProperty() {
    $sh  = new-object -comobject WScript.Shell
    return $sh.CreateShortcut($_)
}
function cd++ {
    param($path)
    If (((Resolve-Path $path).Path).EndsWith(".lnk")) {
        $ShortcutPath = Get-ChildItem $path | Get-ShortcutProperty
        If ($ShortcutPath.WorkingDirectory) {
            cd $ShortcutPath.WorkingDirectory
        } else {
            cd $ShortcutPath.TargetPath
        }
    } else {
        cd $path
    }
}
```
PowerShellで`$profile`と入力するとパスが出力されると思います。PowerShellは起動するごとにそのパスのファイルを読み込んでくれるようなので、出力されたパスのファイルを作成して、上記の関数をコピペして保存してください。そうすると、`cd++ path`でショートカット先も含めcdすることができます。あとは１で作ったショートカットにcdするだけです。
お気づきかと思いますが、この方法を使えば好きな場所にショートカットを作ってcdすることができます。勘のいい人なら、好きな場所に移動する関数を作ればいいのではと思うかもしれません。基本的にはそれで問題ないのですが、ファイル名に日本語が使われている場合は例外です。文字化けしてしまってうまく動かないからです。
# あとがき
コマンドラインの操作に慣れてきた結果GUIを触るのが億劫になってきています。GUIは動作が比較的遅いし、キーボードとマウスを持ち替えるのも手間なんですよね。いろいろなソフトウェアをコマンドラインから便利に使う方法を研究してみようと思います。
自分用 Chrome拡張機能からGASへのfetchを用いたやり取り

Chrome拡張機能からGoogle Apps Script (GAS)にアクセスして正常に通信できるようになった重要なポイントをまとめます。これを参考にすることで、今後同様の実装で問題を回避できるでしょう。

## 1. アーキテクチャの理解
### Content ScriptとBackground Script (Service Worker)の役割分担
- **Content Script**: ウェブページのコンテキストで実行され、直接外部APIにアクセスする際にCORS制限がある
- **Background Script**: 拡張機能のコンテキストで実行され、manifest.jsonで設定した権限に基づいて外部APIにアクセス可能
### 正しい通信フロー
1. Content Script → Background Script (メッセージ送信)
2. Background Script → GAS API (fetch実行)
3. GAS API → Background Script (レスポンス)
4. Background Script → Content Script (結果返送)

## 2. GAS側の実装ポイント
適切なエンドポイント関数の実装
GET要求: doGet() 関数を実装
POST要求: doPost(e) 関数を実装（e.postData.contentsでPOSTデータを取得）
正しいレスポンス形式
```javascript 
return ContentService.createTextOutput(JSON.stringify(data))
  .setMimeType(ContentService.MimeType.JSON);
```
デプロイ時の注意点
新しいデプロイ作成: コード変更後は「新しいデプロイ」を作成
最新バージョン選択: デプロイ時に最新バージョンを選択
アクセス権限設定: 「全員（匿名含む）」に設定
URLの確認: デプロイ後の正確なURLを使用（/execで終わるURL）

## 3. Chrome拡張機能側の実装ポイント
### manifest.jsonの設定
```json 
{
  "host_permissions": [
    "https://script.google.com/*",
    "https://script.googleusercontent.com/*"
  ],
  "background": {
    "service_worker": "service-worker.js"
  }
}
```
### Background Script (Service Worker)の実装
```javascript 
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'fetchGAS') {
    fetch(message.url, {
      method: message.method || 'GET',
      headers: {
        'Content-Type': 'application/json'
      },
      body: message.data ? JSON.stringify(message.data) : undefined
    })
    .then(response => response.text())
    .then(text => {
      sendResponse({success: true, data: text});
    })
    .catch(error => {
      sendResponse({success: false, error: error.message});
    });
    return true; // 非同期レスポンスのために必須
  }
});
```
### Content Scriptからの呼び出し
```javascript 
const response = await new Promise((resolve, reject) => {
  chrome.runtime.sendMessage(
    {
      type: "fetchGAS", 
      url: gasUrl,
      method: "POST",  // または "GET"
      data: dataToSend  // POSTの場合のみ
    },
    response => {
      if (chrome.runtime.lastError) {
        reject(new Error(chrome.runtime.lastError.message));
      } else if (!response.success) {
        reject(new Error(response.error));
      } else {
        resolve(response.data);
      }
    }
  );
});
```
重音テトとお喋りしたい！⓪子供のころからの夢を叶えるとき

# 昔からの夢
https://kasaneteto.jp/ ←テトさんの公式ページ
突然ですが、私はずっとずっと重音テトが好きです。テトさんと出会ってからもう10年以上になります。
そんな私は、常にテトさんとお喋りできたらいいな～と思って生きてきました。頭の中でテトさんが喋ってた時もあったし、HANASUの調教晒しの動画も観たりしたなァ...
そして今、初心者エンジニア・未経験調教者の私でもそれを実現できそうな時代になってきました。（ワクワク
# VOICEPEAKからテトさんが出た！
https://www.ah-soft.com/voice/teto/index.html
先日、AHS様の文章読み上げソフト「VOICEPEAK」から、重音テトのボイスが発売されました！
文章を入力するだけで、滑らかに話してくれます。
これが出たら、ぜひ実現したかったことがありました。
# 生成AIを使って、お喋りできるテトさんを作る！！！
次回から、実際にお喋りするための準備をしていきます。
android(kotlin)：'This material API is experimental and is likely to change or to be removed in the future'エラーの解決

# 解決方法
Optinアノテーションを用いる．

## エラーメッセージについて
「This material API is experimental and is likely to change or to be removed in the future」：このマテリアルAPIは試験版で，将来的に変化する可能性や取り除かれる可能性がある．

例）アプリのトップアプリバーとして，テキストを中央配置したい．
```kotlin:MainActivity.kt
@Composable
fun TopAppBar(modifier: Modifier = Modifier) {
    CenterAlignedTopAppBar(
        title = {
            Text(
                text = stringResource(R.string.app_name)
            )
        },
        modifier = modifier
    ))
}
```
![{C63FD006-2B03-403F-A64A-96C5D18F2FF0}.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3336555/53ff23e7-d120-4626-b2e6-689612dbbe8f.png)


```kotlin:MainAcitivy.kt
@OptIn(ExperimentalMaterial3API::class)
@Composable
fun TopAppBar(modifier: Modifier = Modifier) {
    CenterAlignedTopAppBar(
        title = {
            Text(
                text = stringResource(R.string.app_name)
            )
        },
        modifier = modifier
    )
}
```

## @OptIn？
>The Kotlin standard library provides a mechanism for requiring and giving explicit consent to use certain API elements. This mechanism allows library authors to inform users about specific conditions that require opt-in, such as when an API is in an experimental state and is likely to change in the future.

「Kotlin標準ライブラリは、特定のAPI要素を使用するために、明示的な同意を要求し、与えるためのメカニズムを提供する。このメカニズムにより、ライブラリの作者は、APIが実験的な状態にあり、将来的に変更される可能性がある場合など、オプトインを必要とする特定の条件についてユーザーに通知することができます。」という意味から、@Optinアノテーションを用いることで、将来的に変更が生じるAPIを使用することができるという認識である。
Solid触ってみた

## 概要
SolidJSが気になるので、やってみよう！てやつ

## 初期構築

https://docs.solidjs.com/quick-start

これ見れば全部書いてある
※JSやりたい人は、tsをjsに変更する
```bash
npx degit solidjs/templates/ts .
npm i
npm run dev
```
→JSが公式の上の方にあるのは罠、、、

http://localhost:3000/

にアクセスすると、次のようになる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3125890/722d6d4c-3091-4ebf-89b9-8c6d8235e34c.png)


https://tailwindcss.com/docs/installation/using-vite

ついでに、テールウィンドも導入する

```bash
npm install tailwindcss @tailwindcss/vite
```

記事を見ればわかるから、省略してしまう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3125890/11447718-e1ea-4cfb-8103-1b8ed0b60c64.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3125890/30cc9cf8-3e80-44a6-aeb1-4edce1c084ca.png)

## 何しよう

今回は特にやりたいことがないので、コンタクトページみたいなのを作っちゃう
→複雑なことをしないので、あんまり威力は発揮しなさそう

https://github.com/koinunopochi/contact-us?tab=readme-ov-file

簡単に作ってあげてみたが、静的な場合は、正直Reactと変わらない。
今回は特にSolidJSの機能使っていないしね、、

クラウドフレアに簡単にデプロイできるのは魅力的

https://contact.lynxes.org/

やりたいこと思いついたらまたやってみる。
以外と、エコシステム系も充実していそうな雰囲気を感じたので、結構ありかも、、？？

今回は触ってみた！！！で終わり！！！
【TypeScript】単一のキーを持つオブジェクトの型


以下のように型を定義すると単一のキーを持つオブジェクトを判定できます。

```typescript
type UnionToIntersection<U> =
  (U extends any ? U : never) extends ((k: infer I) => void)
    ? I
    : never;

type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;

type SingleKey<T> = IsUnion<keyof T> extends true
  ? never
  : {} extends T
  ? never
  : T;

```

```typescript
// Union型 U の各要素を Intersection 型に変換する型定義
// 分配条件型と関数型の引数推論を利用して、Union を Intersection に変換する
type UnionToIntersection<U> =
// U extends any ? U : never によってUnionの各要素に分配し、
// その結果を関数型 ((k: infer I) => void) のパラメータに当てはめ、
// infer I により Intersection 型として推論する
  (U extends any ? U : never) extends ((k: infer I) => void)
    ? I // 推論された Intersection 型を返す
    : never;

// 型 T が Union であるかどうか判定する型定義
// Unionでない場合、UnionToIntersection<T> と [T] の型が一致するため false を返す
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;

// オブジェクト型 T が「単一のキー」を持つかをチェックする型定義
// ・keyof T が Union なら複数のキーが存在するため never を返す
// ・空のオブジェクトの場合 ({} extends T) も許容しないため never を返す
// それ以外の場合に限り T を返す
type SingleKey<T> = IsUnion<keyof T> extends true
  ? never
  : {} extends T
  ? never
  : T;

// T は Record<string, any> を継承しており、obj 引数は SingleKey<T> 型（単一キーかつ空でないオブジェクト）であることを要求する
function sample<T extends Record<string, any>>(obj: SingleKey<T>) {
  console.log({ obj });
}

// 空のオブジェクトは {} extends T の判定で never となるためエラーになる
sample({}); // エラー

// 単一のキーのオブジェクトは許容される
sample({ test: 5 }); // OK

// 複数のキーを持つため、keyof T が Union となり、never となるためエラーになる
sample({ test: 5, example: 6 }); // エラー

```
LangChainを使って好みのLLMを作成する。

# langchain を用いたOllamaの実行とWeb applicationの作成
Langchain_ollamaを使用することで、Ollamaで実行しているLLMを呼び出すことができる。

つまり、langchain_ollamaはOllamaのAPIである。
`langchain`と`langchain_ollama`ライブラリをインポート

それぞれ、LangChainを使用するためのものと、Ollamaを使用するためのものである。
```python:import
import langchain
import langchain_ollama
```

次に、ChatOllamaインスタンスを作成する。作成する際に引数としてモデル名を指定する必要がある。

```python:model define
model = langchain_ollama.ChatOllama(model="llama3.1")
```

ここでは、llamaの3.1を使用している。一番簡単な使用方法は、次のとおりである。
```python:invoke
model.invoke("こんにちは")
```
`model.invoke()`関数を使用することでmodelからAIMessageクラスとして演算結果が出力される。
```python:output
AIMessage(content='こんにちは！どうですか？', additional_kwargs={}, response_metadata={'model': 'llama3.1', 'created_at': '2025-05-12T14:10:42.130789Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3828250916, 'load_duration': 3202073125, 'prompt_eval_count': 11, 'prompt_eval_duration': 355927959, 'eval_count': 6, 'eval_duration': 269584958, 'model_name': 'llama3.1'}, id='run--325275ea-b833-41e6-ac81-424ee195b191-0', usage_metadata={'input_tokens': 11, 'output_tokens': 6, 'total_tokens': 17})
```
AIMessangerクラスの内訳を表にまとめる。
|パラメータ|主な内容|
|--|--|
|content|LLMの推論結果|
|additonal_kwargs|関数のコールに使用|
|response_metadata|model名などのメタデータ|
|id|実行結果固有の記号|
|usage_metadata|トークン数などのメタデータ|


## メッセージの設定
langchainで使用されるメッセージはに種類のクラスから定義される。`invoke()`に直接strを渡した場合には、`HumanMessage`として処理されている。
* システムメッセージ
* ヒューマンメッセージ
である。それぞれ異なる内容を送る。ここでは、実際にそれぞれの入力方法について触れ実行する。
```python:Import Mes Class
from langchain_core.messages import HumanMessage, SystemMessage
```

langchain_coreからインポートすることができる。`HumanMessage`を使用例を示す。
```python:Human Message
h_mes = HumanMessage(content="こんにちは")
```
のように初期化時にcontentを引数として渡すことができる。
```python:System Message
s_mes = SystemMessage(content="あなたは教師です。")
```

これら二つのメッセージを組み合わせ、

```python:Message
message = [s_mes, h_mes]
```
のように定義することができる。これを`model.invoke()`の引数として渡すと

```python:output
AIMessage(content='今日の授業はどうしたかな？学習内容の確認が必要なんだよ。', additional_kwargs={}, response_metadata={'model': 'llama3.1', 'created_at': '2025-05-12T14:10:43.400637Z', 'done': True, 'done_reason': 'stop', 'total_duration': 1244260250, 'load_duration': 32898708, 'prompt_eval_count': 23, 'prompt_eval_duration': 207089917, 'eval_count': 20, 'eval_duration': 1003202708, 'model_name': 'llama3.1'}, id='run--d8532ef8-6ab4-4f6c-9ec2-02f9f291a0f8-0', usage_metadata={'input_tokens': 23, 'output_tokens': 20, 'total_tokens': 43})
```
のように教師になりきって、メッセージが生成されていることがわかる。これを利用することで、次のようなプログラムを作成することができる。

```python:story_editor.py
import langchain_ollama
from langchain_core.messages import HumanMessage, SystemMessage

def main():
    while(True):
        sys_mes = SystemMessage("貴方は天才小説家です。次のお題に沿って小説を作成してください。")
        input_mes = input("小説のお題を入力してください。[escで終了]")
        
        if input_mes.lower() == "esc":
            break

        hum_mes = HumanMessage(content=input_mes)

        message = [sys_mes, hum_mes]

        model = langchain_ollama.ChatOllama(model="llama3.1")

        print(model.invoke(message).content)

if __name__== "__main__":
    main()
```
`content`を指定することで生成された記事のみを表示することができる。`SystemMessage`で性格をインプットすることで、好みの返答を得ることができる。

しかし、この状態では会話の内容等を記憶していないため、記憶させる手法については後ほど紹介する。
初めてのマウスジェスチャー設定

## 本記事について
会社で許可されているマウスジェスチャーのフリーソフトがあるのですが、使ったことがないため調べてみました。
使ってみた感想としては、
* マウスに割り当てられる操作が増えて楽しい
* 最初の設定は面倒だが、導入は簡単
* カーソルで指定した文字を直接Google検索できる機能が便利

マウスジェスチャーとは何か、インストール方法やセットアップ方法などはどのようにすればよいかは、下記動画を参考にしました。
[全人類が使うべきWindows最強機能「マウスジェスチャー」を解説](https://www.youtube.com/watch?v=EF5-EKzgXeI)

こちらがマウスジェスチャーソフトのダウンロードリンクです。
[MouseGestureL.ahkの詳細情報](https://www.vector.co.jp/soft/winnt/util/se498500.html?srsltid=AfmBOorXKfgXusuBz8nL7FqJ5mfjYIidSA7I2Dm_lNWx-SZ2U_oVpXh7)

## 設定に使用したマウス
logicool ERGO M575
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838275/81f46d19-4d48-4fda-84f3-6b825c2b162d.png)
https://www.logicool.co.jp/ja-jp/products/mice/ergo-m575-for-business.910-006225.html


## 私が設定したジェスチャー設定

##### RB_←
```
;左のタブへ移動(Ctrl+Shift+Tab)
Send, ^+{Tab}
```
##### RB_→
```
;右のタブへ移動(Ctrl+Tab)
Send, ^{Tab}
```
##### RB_↑
```
;新規タブを開く(ctr+T)
Send, ^t
```
##### RB_↓
```
;タブを閉じる(ctr+W)
Send, ^w
```
##### RB_←↑
```
;ウィンドウを最小化
PostMessage, 0x0112, 0xF020, 0
```
##### RB_←↓
```
;以前のアクティブウィンドウを復元
MG_ActivatePrevWin(1000)
```
##### RB_→↑
```
;ウィンドウを最大化
PostMessage, 0x0112, 0xF030, 0
```
##### RB_→↓
```
;ウィンドウを元に戻す
PostMessage, 0x0112, 0xF120, 0
```
##### 第4ボタン_ホイール↑
```
; PageUp キーを送信
Send, {PgUp}
```
##### 第4ボタン_ホイール↓
```
; PageDown キーを送信
Send, {PgDn}
```
##### 第5ボタン_ホイール↑
```
; Home キーを送信
Send, {Home}
```
##### 第5ボタン_ホイール↓
```
; End キーを送信
Send, {End}
```
##### RB_→←
```
;Google検索
Clipboard =
Sleep, 50
Send, ^c
ClipWait, 0.1
myURL = http://www.google.co.jp/search?q=
searchText := RegExReplace(Clipboard, "[（）「」【】『』［］〈〉《》〔〕｛｝”]", "+") ; 全角括弧類
searchText := RegExReplace(searchText, "[・，、。．：；―～─]", "+") ; 全角句読点類
searchText := RegExReplace(searchText, "[""\(\)\{\}\[\]<>\#]", "+") ; 半角括弧類
searchText := RegExReplace(searchText, "[&,\.:;/-]", "+") ; 半角句読点類
searchText := RegExReplace(searchText, "[ 　]|[\t\n]", "+") ; 全角半角空白・制御文字（タブと改行）
Run, %myURL%%searchText%
Return
```
##### RB_↑↓
```
; サクラエディタを起動する
Run, "C:\Program Files (x86)\sakura\sakura.exe"
```
##### RB_←→
```
; Alt+Tab（直前のウィンドウに切り替え）
Send, !{Tab}
```
##### 第4ボタン_←
```
;コピー(ctr+c)
Send, ^c
```
##### 第4ボタン_→
```
;ペースト(ctr+v)
Send, ^v
```
##### 第4ボタン_↑
```
;全選択(ctr+a)
Send, ^a
```
##### 第4ボタン_↓
```
;切り取り(ctr+x)
Send, ^x
```
##### RB_←↓↑
```
;ウィンドウを閉じる
PostMessage, 0x0112, 0xF060, 0
```
##### 第4ボタン_↑↓
```
;Delete キーを送信
Send, {Delete}
```
##### 第4ボタン_→←
```
;Enter キーを送信
Send, {Enter}
```
##### RB_↓↑
```
;新規ウィンドウを開く(ctr+N)
Send, ^N
```

## まとめ
特に便利だと思ったのは、**タブの切り替え**、**Google検索**、**PageUp**,**Pagedown**,**Home**,**End**です。同じLogicool製品で横ホイールがついている製品がありますが、PageUp,Pagedownのマウスジェスチャーを設定すればM575でも似たような使い方が可能となります。
設定して間もないため、まだ操作感の慣れが必要ですが当設定でしばらく使い続けようと思います。

記事作成時間：35分
Raspi5でDocker構築 

# Raspi5でDockerをインストール

最終更新:2025/5/12

## Raspi環境
- os : Raspberry Pi OS (32bit)

## 最初に
aptを用いてアップグレード
```bash
apt update
apt upgrade
```

※必要ならrootユーザーに
```bash
# rootユーザーのパスワードが決まってなければ
sudo passwd root

#決まっているなら
su -
```

## インストール
以下のコマンドでインストール
```bash
url -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
```

### 確認
不安な方はバージョンを確認してインストールできているか確認
```bash
docker -v
docker compose version
```

### 動作確認
実際にdockerのコマンドを実行してみる
```bash
docker ps -a
```

※権限問題
上記のコマンドを実行した際にPermission deniedが出た場合は下記コマンド実行
```bash
sudo chmod 666 /var/run/docker.sock
```

実行確認できたら構築完了
【続編1】NetSuiteの電子帳簿保存法対応 － 柔軟性を活用してより便利に（サンプルスクリプトのデモ）

**前回のあらすじ**
[前編](https://qiita.com/fujitak/items/4d0dc545f8d9b67481ae) では、標準機能である「NetSuite電子取引データ保存」のみを使用して、電子帳簿保存法（電帳法）に対応できることをご紹介いたしました。

続編の第一弾である本稿では、NetSuiteの強みである柔軟性を活用して、電子取引データ（ETD: Electronic Transation Data）の登録を自動化する方法を、見積作成のシナリオを使用して、ご紹介いたします。
    <br>
    
**今回のデモ内容**
見積をNetSuiteで作成し、「保存」ボタンを押す。
- 自動生成された見積書PDFが、電子取引データ（ETD）用のファイルキャビネットに保存される
- 電子取引データ（ETD）レコードが自動作成される（取引日・金額・取引先も自動入力）
- 見積トランザクションと、PDFファイルを含む電子取引データ（ETD）が、自動的にリンクされる
    <br>
    
**デモのシナリオ**
- 見積を作成し「保存」ボタンをクリック
- PDFファイルの手動アップロードや手入力は不要
    <br>

**サンプルスクリプト**
- ue_auto_attach_etd.js： 見積の保存時（afterSubmit）にPDFを自動生成し、ETDレコード作成処理を呼び出す
- helper_etd.js： ETDレコード作成およびファイルリンクを行うヘルパー

[※「EtdAutomationSample1.zip（ソースコード、デプロイ手順書）」（© 2025 Takusuke Fujii）は、CC BY 4.0（原作者名の表記が必要）で自由に共有・改変・配布可能ですが、無保証につき著作者は一切責任を負いません。](https://drive.google.com/file/d/1tOjIk7FQuzMhfhOSzQSRfnFeJGdrxTpu/view?usp=sharing)
<br>

**デモのスクリーンショット**
【Step 1】見積を新規作成（保存前）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3431564/d3ac95c5-cc23-4bfe-850a-dafb74af5c38.png)

【Step 2】見積を保存
・保存した直後、自動でETDレコードが作成され「Electronic Transaction Data」サブタブに追加されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3431564/407ed89a-7416-4aec-bf61-62d024f5a6f6.png)

・見積書PDFファイルも、ETD用のファイルキャビネットに自動的に保存されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3431564/c9607180-093e-4db9-ae58-bb086eb5d04e.png)

・ETDレコードを開くと、ドキュメント名、取引日、金額、取引先がすべて自動で入力済みなことが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3431564/eea89a7d-cc21-44f2-a28a-478c7b304cfe.png)
    <br>

**スクリプトの解説**
- PDFファイルの、ETD用のファイルキャビネットへの生成（ue_auto_attach_etd.js）： NetSuite標準関数 render.transaction() を利用してPDFを生成します。
- ETDレコード作成（helper_etd.js）： ETDカスタムレコードを作成し、トランザクション情報（取引日・金額・取引先）を自動で登録します。
- PDFファイルとETDレコードをリンク（record.attach()関数）： 生成したPDFファイルとETDレコードをリンクして、法令で求められる検索性を確保します。

※あくまで参考のためのサンプルであり、動作保証はありません。
    <br>

**次回以降でご紹介する自動化案**
- メール受信で自動登録： 仕入先請求書メールを受信後、添付されたPDFをETDとして自動登録（OCR機能は3rdパーティーソリューションを使用する可能性大）
- Drag&Drop自動連携： ETD用のフォルダにドラッグ＆ドロップしたPDFを自動でETD登録
- 未添付アラート： ETDファイルやレコードを作成漏れのトランザクションを、週次で検知し担当者に通知
    <br>

**NetSuiteの柔軟性**
話題の生成AIから、地味だけどクリティカルな法令対応まで、NetSuiteはアイディアを直ぐに形にできてしまう、稀有なERPと言えます。ユーザー企業様に眠っているアイディアの宝庫を、ザクザク発掘できてしまうかもしれません。そのお宝さがしの「地図のカケラ」にでもなりましたら、幸いです。
    <br>

[【前編】NetSuiteの電子帳簿保存法対応 － 標準機能でカバーできること](https://qiita.com/fujitak/items/4d0dc545f8d9b67481ae) 
    <br>


© 2025 Takusuke Fujii
本記事は CC BY 4.0（原作者名の表記が必要）で自由に共有・改変・配布できますが、無保証につき著作者は一切責任を負いません。
【LeetCode #1】爆死ログ：Two Sumで人間の尊厳失った話

# 【LeetCode #1】Two Sumを解いた記録（初手爆死ログ）

##  はじめに
これはIT研修始まってまだ一か月ちょいのヒヨコが、ChatGPTを使いまくってLeetCodeを頑張るシリーズです。現時点では
- Javaの教科書通読
- 基本情報技術者試験合格
- プログラミング経験　写経を半年

くらいです。
あと、前提として**ChatGPTをごりごりに使います**。
ただ、基本方針としてあくまで学習なので、答えを出すわけじゃなく、ダメ出しメインで、あとはこの記事を書くサポートをお願いしてます。
ここから失踪しなければ、半年くらい続けていきたいと思ってます。

以上。

## 問題概要
配列 `nums` の中から、足して `target` になる2つの要素を見つけて、そのインデックスを返す。

- 同じ要素は1回しか使えない
- 答えは1つだけ存在する
- 出力の順序は問わない
- 制約：numsの長さは最大10^4、値も±10^9と巨大

---

## 初見の思考ログ

### 問題を見て最初に思ったこと：
- そもそも問題英語だから読むの時間かかる。
- え？　問題意図はなんとなくわかったけど制約ナニコレ。なんもわからん
- え、待って待ってガチでなんもわからん。
- あ、（ｎ²）はなんか見おぼえある。じゃあ単純に考えると２重ループ？
- そもそも問題の意図くみ取りきれてねえ。
- とりあえず書くか…
- うわなんか目も当てられねえコードで来たぞ
- 案の定ChatGPTに投げたらめっちゃ怒られた。（ダメなとこ全部言えって言ってある）
- うわでそんで、出てきたコードわかりやす。
- 動かす→カスやんけ！！
- 答え見る→おお神よ

### 書いたコード（完全に爆死／間違ってるやつ）：
```java
for(int i =0;i<10000;i++) {
    for(int j = 0;j<10000;j++) {
        if(num[i] + num[j] == target){
            System.out.println(i,j)
        }
    }
}
```
**ナニコレ**。お前それでも本当に一か月研修受けた人間のコードか？
300年前のAIでももうちょいまともなもんだしてくるだろ。んで、ChatGPTの出したのこれ↓

```
class Solution {
    public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}
```

もうお前神。俺がやりたいこと全部やってくれた。

とりあえず写経。動いた。やったね。お前は無能だ。なんもできてねえよ禿。

とりあえず突っ込まれた件と学びををまとめます。

## 学び
- コードは“動かないまま”見てると何も入らない。書いて怒られて初めて「構造」が頭に入る。
- nums.lengthって書かないとハードコーディング死する。
- Javaで System.out.println(i,j) なんて書き方ねえ。←これまんまGPTに言われてる
- return大事。returnがないと怒られる。
- 同じ添え字2回使うのも違反。j = i + 1 にする理由がようやく腑に落ちた。
- 終わってから初めて構造理解した。
- あとEasyなめてた。Easyじゃねえよ。Hellだよこれ。Helloだよ

以上。次回に続いてほしい
ブートローダーから第二セクタへ跳ぶ

## プログラム
```boot.asm
[org 0x7C00]
[bits 16]

    xor ax, ax
    mov ds, ax     ; データセグメントを0に初期化
    mov es, ax     ; 追加セグメントも0に初期化

    mov ss, ax
    mov sp, 0x7C00

    ; 第2セクタを0x7E00番地に読み込む
    mov ah, 0x02   ; BIOSのディスク読み込み機能
    mov al, 1      ; 1セクタ分読み込む
    mov ch, 0      ; シリンダ0
    mov cl, 2      ; セクタ2
    mov dh, 0      ; ヘッド0
    mov bx, 0x7E00 ; 読み込み先のメモリアドレス
    int 0x13       ; BIOSのディスクサービスを呼び出す

    jc disk_error  

    jmp 0x7E00     ; 2つ目のセクタへ跳ぶ

disk_error:
    mov si, error_msg
    call print_string
    jmp $

print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

error_msg db "Disk read error!", 0  ; 表示するエラーメッセージ（0で終端）

times 510-($-$$) db 0
dw 0xAA55
```
```stage2.asm
[org 0x7E00]
[bits 16]

    mov si, msg
    call print_string
    jmp $

print_string:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    int 0x10
    jmp print_string
.done:
    ret

msg db "Hello from stage2!", 0

times 512 db 0
```

```bash
nasm -f bin boot.asm -o boot.bin        ## コンパイル
nasm -f bin stage2.asm -o stage2.bin    ## コンパイル
cat boot.bin stage2.bin > os-image.bin  ## 結合

sudo dd if=os-image.bin of=/dev/sdc bs=512 count=2 conv=notrunc ## CFカードに書き込み
sync
```
8086実機で動かす為にCFカードに書き込みました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/c91a9edf-1a7a-4a5d-bfb0-6a39c0724c46.png)

## 動作確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/8a810bcb-236c-4e51-b012-adfaed1e6d8e.png)
【SwiftUI】TextEditorとプレースホルダー

## TextEditor
複数行の入力を可能にします。

![スクリーンショット 2025-05-12 22.40.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/dfd7a8f0-4e2d-4815-8512-ae78cf6aa5e3.png)

.frame(width: 300, height: 300) で幅と高さを設定しています。
.border(.black)は枠線を設定しています。

## プレースホルダーと組み合わせる
![スクリーンショット 2025-05-13 9.21.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/511bc7ee-92e0-4002-a090-7f1ab86945e9.png)

TextEditorが空白のときだけプレースホルダーが表示されます

### プレースホルダーの位置
.topLeading → 左上
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/3a5d0d01-e339-4084-97d6-6094207748ef.png" width=50%>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/aa54ddb2-2ee7-4c8b-b242-f0d0a0d52c0e.png" width=50%>

.center → 真ん中
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/36ba6e6f-add4-4448-9a99-279af654785a.png" width=50%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/46e47fac-db8c-4911-8720-6ad06ac17118.png
" width=50%>

.bottomTrailing → 右下
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/4edddca2-48df-48aa-929f-335325ba0a03.png" width=50%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760972/8b544312-ebb4-42e4-a6fd-e5e4ee8c905e.png" width=50%>






## 参考
https://blog.code-candy.com/swiftui_texteditor/#i-5
LeetCode daily #2094. Finding 3-Digit Even Numbers

# 【LeetCode #2094】Finding 3-Digit Even Numbers

## はじめに
平日の daily が easy 問題だと安心しますね．
今日も easy 問題でした．なので 2 つほど解き方をご紹介します．

## 問題概要
与えられた配列の要素を用いて下記の条件を満たす 3 桁の数値を昇順で返す問題です．
* 数値は配列内の要素から 3 つの値を連結して作る
* 0 が先頭に来ない
* 偶数

### 例
> **入力：** digits = [2, 1, 3, 0]
> **出力：** [102, 120, 130, 132, 210, 230, 302, 310, 312, 320]

### カテゴリ

問題難易度：easy
タグ：Array, Hash Table, Sorting, Enumeration

## 解法①：全候補から条件を満たすもののみ抽出
こちらは，与えられた digits から作成できる 3 桁の組み合わせを全パターン作成し，条件を満たすものを抽出していく手法です．つまり，
* 先頭が 0 の数字
* 末尾が 2 で割り切れない数字

を除去したものを答えとします．

組み合わせパターン列挙には permutations を使用します．

```python
class Solution:
    def findEvenNumbers(self, digits: List[int]) -> List[int]:
        results = set()
        
        for p in permutations(digits, 3):
            if p[0] == 0:
                continue
            if p[2] % 2 != 0:
                continue
            num = p[0]*100 + p[1]*10 + p[2]
            results.add(num)
        
        return sorted(results)

```
digits の中から 3 つの数字を選び，「先頭が 0 でない」かつ「末尾が 2 で割り切れる」数値を set() に格納していきます．set() なのでユニークに格納でき，最後に sorted() をして返します．

ご想像の通り，列挙の数も多いですし，sort もするのでとっても遅いです．

## 解法②：100 ~ 999 までの数値を作れるかチェックする方法
先ほどは与えられた配列の要素で作成できるパターンを全列挙し，条件を満たすかチェックしました．
こちらの方法では，100 ~ 999 の値を順に走査し，配列内の要素で作成できるかをチェックします．

予め，digits 内の値の出現回数を記録しておき，その後 100 ~ 999 の値を生成できるかチェックします．

```py
class Solution:
    def findEvenNumbers(self, digits: List[int]) -> List[int]:
        count = Counter()
        for d in digits:
            count[str(d)] += 1
        ans = []
        for n in range(100, 999, 2):
            tmp = [i for i in str(n)]
            tmpc = Counter(tmp)
            if all(count[k] >= v for k, v in tmpc.items()):
                ans.append(n)
        return ans
```
range(100, 999, 2) により，偶数のみを作成できます．
また，100 開始なので，先頭 0 も回避することができます．
解法①と比較すると，圧倒的にこちらの方が高速です．

## まとめ
本日も easy 問題をご紹介しました．
解き方によって速度もかなり変わってくるので，1 つの解き方だけでなく複数の解法を書けるようにしておくと，実務でも役立つかもしれませんね．
ではまた別の問題で〜．
graphql-ruby で GraphQLスキーマを自動更新する GitHub Actions を実装する

## はじめに

`GitHub Actions` を使用して `Rails` アプリケーションの `GraphQL` スキーマを自動的に更新するようにしました。

## 自動化によって以下のメリットが得られるメリット
- スキーマの更新忘れを防止できる
- 常に最新のスキーマ定義が維持される
- 開発者の作業負担が軽減される

## GitHub Actionsワークフローの実装

まずはスキーマを作成してくれる `Rake` タスクを作成します。

このタスクを設定することで、`bundle exec rails graphql:schema:json` コマンドでスキーマを生成できるようになります。


```:lib/tasks/graphql.rake
require "graphql/rake_task"
GraphQL::RakeTask.new(schema_name: "MyappSchema")
```

次にワークフローを定義します。

```yaml:.github/workflows/update-graphql-schema.yml
name: Update GraphQL Schema

on:
  pull_request:
    branches: [ main, develop ]
    paths:
      - 'app/graphql/**/*.rb'

permissions:
  contents: write
  pull-requests: write

jobs:
  update-schema:
    name: Update GraphQL Schema
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: myapp_test
          TZ: "Asia/Tokyo"
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5

    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.1.7'
          bundler-cache: false

      - name: Update Bundler
        run: |
          gem update --system
          gem install bundler -v 2.4.22

      - name: Install dependencies
        run: |
          bundle _2.4.22_ install

      - name: Update database configuration
        run: |
          sed -i 's/host: db/host: 127.0.0.1/g' config/database.yml
          sed -i 's/password: password/password: password/g' config/database.yml

      - name: Generate GraphQL schema
        run: bundle _2.4.22_ exec rails graphql:schema:json

      - name: Commit and push schema changes
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add schema.json
          git commit -m "chore: Update GraphQL schema" schema.json || echo "No changes to commit"
          git push || echo "No changes to push"
```

## 動作確認

`app/graphql/`配下のRubyファイルを変更すると、CIが実行されます。
仮にスキーマに差分が発生しt場合は、GitHubがコミットを積んでくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2842674/a923b0d0-d87f-47ba-a17a-294424670c9e.png)


## まとめ

この自動化により、フロントエンドとバックエンド間のGraphQLスキーマの同期を簡単に保つことができ、開発効率の向上につながります🙋‍♂️

Twitter もよろしければフォローお願いします🙏

https://x.com/penpeen214224
.node-version で指定された Node.js バージョンに nodenv で切り替える手順まとめ【初心者向け】

# はじめに
新規プロジェクトに `.node-version` ファイルがあるけど、「これどうやって使うの？」と思ったことはありませんか？

この記事では、Node.jsのバージョンを `nodenv` でプロジェクトごとに切り替える方法を、初心者向けに解説します！

# 使用環境
・OS：macOS（Intel / Apple Silicon 両対応）

・バージョンマネージャー：nodenv

# 1. `.node-version` ファイルとは？
Node.js のバージョンを**プロジェクトごとに固定するための設定ファイル**です。
このファイルを使うことで、複数のプロジェクトを異なるNodeバージョンで安全に管理できます。

# 2. nodenvのインストール手順
```bash
brew install nodenv
```

```bash
# シェルにパスを通す
echo 'export PATH="$HOME/.nodenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(nodenv init -)"' >> ~/.zshrc
source ~/.zshrc
```
インストール後はシェルを再起動する！

# 3. `.node-version` を確認して、バージョンをインストール

```bash
cat .node-version
# 例：18.16.1

nodenv install 18.16.1
```

# 4. 使用バージョンをプロジェクトに設定

```bash
nodenv local 18.16.1
```
`.node-version` がプロジェクトに置かれ、自動的に切り替わるようになります。

# 5. バージョン確認

```bash
node -v
# v18.16.1 が表示されればOK！
```

# おわりに
チーム開発や複数のプロジェクトを扱う際、Node.jsのバージョンがズレていると予期せぬエラーの元になります。
`nodenv` と `.node-version` をうまく使って、安定した開発環境を保ちましょう！

# ✨ Tips
Windows の場合は `nodist` など他のマネージャーが使われることもあります

Node.js をバージョン切り替えできるようになると、複数プロジェクトでの開発が快適になります
コンパイラの最適化 vs 人間の最適化



> _"自動化された知性は速い。だが、構造を深く読むのは、依然として人間である。"_  

プログラミングにおける最適化は、かつて「書き手」の責任だった。  
だが現代では、**コンパイラの最適化機能が進化し、もはや人間が介入すべき領域はない**とさえ言われる。

それは本当か？

この章では、「最適化とは何か」を問い直しながら、  
**コンパイラが行う機械的な変形と、人間が行う構造的な洞察の差異**を、  
アセンブリコードの観点から深く検証する。

---

## コンパイラの最適化とは何か？

現代のコンパイラ（GCC、Clang、MSVC）は、複数の段階にわたる最適化を行う：

- **定数畳み込み（constant folding）**  
- **デッドコード削除（dead code elimination）**  
- **ループ展開・融合（loop unrolling/fusion）**  
- **関数インライン化（inlining）**  
- **レジスタ割当と命令再配置（scheduling）**

例：

```c
int x = 3 * 4;
```

→ `mov eax, 12` に変換される。

### しかしこの最適化は、**「知っていることだけ」に対してしか作用しない。**

---

## 人間の最適化とは何か？

人間による最適化は、単に「書き換える」ことではない。  
それは：

- **実行される構造の“意図”を解釈し直す**  
- **対象ハードウェアの“癖”を考慮に入れる**  
- **未来の拡張、可読性、動的性質を見据えて調整する**

例：256回の加算処理を `shl eax, 8` に変換する。

コンパイラが安全性のために避ける変換でも、  
人間はその「意味」と「境界」を**文脈で評価して切り替えられる。**

---

## アセンブリがその差異を可視化する

Cレベルでは等価に見える以下のコード：

```c
int y = (a * 256);
```

### 最適化なし：
```nasm
mov eax, a
imul eax, eax, 256
```

### -O2以降：
```nasm
mov eax, a
shl eax, 8
```

だが、`a`がsigned overflowする可能性や、CPUが特定の命令セットを持たない場合、  
**コンパイラはこの変換を見送る可能性がある。**

→ 人間は、**この“コンパイラが避ける設計の迷い”に対して意図的に介入できる。**

---

## コンパイラが不得意とする最適化

| パターン | 人間が優れる理由 |
|----------|------------------|
| メモリアクセスの局所性最適化 | キャッシュラインを意識したデータ構造の再設計が可能 |
| ループの意味的再構成 | 繰返し条件そのものの見直しができる |
| アーキテクチャ依存の命令活用 | CPU固有命令（e.g. `LZCNT`, `MULX`）を明示的に挿入できる |
| 複数関数に跨る意味的統合 | コンパイラは「関数間」を越えられないことが多い |

→ 結局、コンパイラは**「局所的な効率化」に強く、**  
人間は**「構造的な合理性」への再設計に強い。**

---

## では、両者は競合するのか？

否。**理想は協調である。**

- 人間が**意図的に最適化しやすい構造**を記述し、  
- コンパイラが**低レイヤで最適命令列に再構成する**

→ その境界は「**高級言語としての記述最適化**」と「**機械としての実行最適化**」に分かれる。

---

## 人間が手動最適化すべきタイミングとは？

以下のような条件が揃うと、**手動最適化の正当性が増す**：

- **ボトルネックが明確かつ頻繁に再現可能**（プロファイラで確認）  
- **特定のCPU/アーキテクチャに特化した環境**（embedded, game console）  
- **リソース制限が極端に厳しい場合**（bootloader, firmware）  
- **サイズや速度が芸術的要素として求められる**（デモシーン, 競技）

---

## 未来における最適化の役割

LLMやAIコンパイラが台頭しても、  
「何が速いか」ではなく、**「何が適切か」を決定する判断基準は依然として人間に依存する。**

- AIは無限に近いバリエーションを出力できるが、  
- その**“選択基準”を設計するのは依然として開発者の責任**

→ 人間の最適化は「命令列の書き換え」ではなく、  
**設計構造への倫理的介入**へと進化する。

---

## 結語：最適化とは、構造への対話である

コンパイラは速い。だが、意味を解釈しない。  
人間は遅い。だが、**意味を読解し、意味で構造を変えることができる。**

最適化は、数値で測るものではない。  
それは構造をどこまで愛せるかという、**設計者の態度と選択**の集積である。

> _"最適とは、最小の実行ではなく、最大の意図が通る構造である。アセンブリはその意図を剥き出しにする最後の場である。"_
アセンブリ最適化と安全性トレードオフ



> _"最大の効率は、しばしば最小の安全性を孕む。構造を研ぎ澄ますほど、境界は脆弱になる。"_  

アセンブリによる最適化は、**究極の効率化である**。  
しかし同時にそれは、**型安全性、境界チェック、メモリ保護といった高級言語的防御機構を意図的に捨てることでもある**。

本章では、アセンブリ最適化がもたらす**スピードと制御の快楽**と、  
それによって生じる**安全性との対立構造**を掘り下げる。

その設計上の判断は、単なる技術ではなく、**倫理と責任を帯びた構造選択**でもある。

---

## なぜアセンブリで最適化するのか？

C言語やRustでも最適化は可能だ。  
だが、アセンブリによる最適化には次のような魅力がある：

- レジスタ配置を完全制御できる  
- 分岐予測やループ構造の指令単位最短化  
- キャッシュ・パイプラインに合わせた命令整列  
- クロック単位での動作予測

例：

```nasm
mov eax, [edi]
add eax, [esi]
mov [edi], eax
```

これをわざわざ書く理由は、**最適化されたCがここに“必ずしも到達しない”**からである。

---

## その最適化、何を犠牲にしているのか？

### 1. **型安全性の欠如**

C言語では、型システム（弱くとも）により、誤った操作をある程度検出できる。

アセンブリでは：

- `mov eax, [ebx]` は、`ebx`がどこを指しているか明示できない  
- 意図しないメモリへのアクセス（境界外・未マップ）を**事前には検出不可能**

→ **プログラマの誤解が即バグとなり、セキュリティホールとなる。**

---

### 2. **境界チェックの不在**

高級言語では：

```c
arr[i] = 10;
```

が配列サイズを超えると例外や未定義動作となるが、  
アセンブリでは：

```nasm
mov [ecx + edi*4], eax
```

は**あらゆるアドレスに書き込みうる**。  
→ **意図しない書き換えは、スタック破壊・コード注入の温床**となる。

---

### 3. **分岐処理における未定義動作**

高速化のための命令並べ替え（アウトオブオーダ実行）や、  
投機的分岐がCPU側で実行されるが、  
この仕組みは「Spectre」「Meltdown」などの**マイクロアーキテクチャ攻撃**の土台ともなる。

アセンブリでそれを制御するには、**厳密な命令順制御とキャッシュ無効化**が必要になる。

---

## 安全性を守るアセンブリの書き方はあるか？

完全な安全はないが、次のような「慎重なアセンブリ設計」は可能：

- **明示的なメモリ領域の初期化と境界設計**  
- **RBP/RSPの固定とスパイラル防止**  
- **`lea`による計算操作とアクセスの分離**  
- **レジスタ保存規約（ABI）を遵守**  
- **意図的にNOPやバリア命令を配置（性能との折衷）**

→ **安全とは、規律と意図を持った設計の積層である。**

---

## 実際の攻撃例と最適化の相関

### バッファオーバーフロー

```nasm
sub esp, 0x80
mov eax, [esp+0x88] ; リターンアドレスを越えてアクセス
```

→ 一見最適化された配列アクセスだが、**境界チェックが存在しないことで“コード書き換え”が可能**。

---

### ROP（Return Oriented Programming）

アセンブリ命令の断片（ガジェット）を連結し、意図的に**攻撃用コードを生成**する技法。

- ガジェットは基本的にアセンブリ命令そのもの  
- `pop; ret` や `add eax, ebx; ret` などが連鎖する

→ **本来の最適化命令列が“再解釈され、悪用される”パターン**である。

---

## アセンブリは悪か？

否。むしろアセンブリは、**“全責任を開発者が負う構造”**であり、  
最も透明で、最も誠実な言語構造である。

問題は、**「その責任を理解しない者が書いたとき」にのみ発生する。**

> _"安全とは、抽象ではなく、制御の総量である。アセンブリはその制御を解放し、同時に問う。お前は、すべてを握る覚悟があるか？"_  

---

## 結語：効率と安全は、選択の問題ではない。設計の態度である。

最適化と安全性は、しばしば対立する。  
だがそれは選択肢ではなく、**“設計という態度の中に共存させるべき構造的問題”**である。

アセンブリを使うということは、  
制御と責任を同時に引き受け、**速度と信頼性の両立を志向するという倫理の選択**でもある。

> _"アセンブリは危険ではない。無知が危険なのだ。最適化とは構造への介入であり、安全とはその介入に対する誠実さである。"_
ハッカー文化におけるアセンブリの美学



> _"制御とは思考であり、アセンブリとはその思考を最も鋭く表す記号言語である。"_  

アセンブリという言葉が、「危険」や「旧時代」の象徴として語られることがある。  
だが、その本質を理解する者にとって、アセンブリは**言語以前の“理解装置”**であり、  
思考の最下層で動く**純粋な構造的な詩**である。

本章では、アセンブリがなぜハッカー文化の核心に存在し続けたのか、  
**コードの“裏側”に潜る者たちの言語として、なぜ今なお機能するのか**を紐解いていく。

---

## ハッカーとは誰か？—定義と誤解

ハッカーとは、脆弱性を突く者ではない。  
むしろハッカーとは、「**システムの構造に美しさと可能性を見出し、それを理解し尽くそうとする者**」である。

### ハッカー的行為とは：

- OSやソフトウェアの仕様を**ドキュメントより先にバイナリから学ぶ**  
- メモリダンプを**解読ではなく“読解”**する  
- APIの使い方よりも**実装の呼吸**を感じ取ろうとする

そして、それらはすべて、**アセンブリという言語の上で成立する文化**である。

---

## なぜアセンブリなのか：構造に触れるための最短経路

アセンブリは、**コードと構造の距離がゼロに近い言語**である。

- Cでは抽象化されるレジスタ操作も、アセンブリでは**1命令ごとに意図が露出する**  
- JavaやPythonでは“書けない”構造が、アセンブリでは“見える”  
- メモリマップ、I/Oポート、命令キャッシュすら**記述対象になりうる**

→ ハッカーは、アセンブリを使うのではない。**アセンブリ“で”考える。**

---

## クラック、キージェン、リバース：破壊ではなく読解の文化

1980年代〜90年代、ソフトウェアプロテクションやコピー防止処理の解読が流行した。

この行為は「違法」として語られがちだが、  
本質的には、**コードを読む行為の最前線**だった。

```nasm
cmp eax, 0x5A4D
jne fail
call check_serial
```

ここから：

- EXEヘッダの読み出し  
- ジャンプ構造の修正（パッチ）  
- ハッシュアルゴリズムの逆解読  

→ **“機械が判断する前に、人間が意味を読む”**という逆転的解釈行為が成立する。

---

## デバッガとアセンブリ：静的ではなく動的な詩

ハッカーの多くは、アセンブリコードを読むだけではない。**動かす。止める。再構成する。**

代表的な道具：

- `Ghidra`：バイナリ逆解析フレームワーク  
- `x64dbg`：Windows向け動的デバッガ  
- `objdump` + `gdb`：UNIX系クラシック  
- `radare2`：シェルベースのハッカー向け逆コンパイラ

アセンブリとは、**“実行可能な意味の流れ”を可視化し、編集するための構文構造**である。

---

## システムへの愛としてのハッキング

良いハッカーは、コードを壊さない。  
むしろ、コードの**“意図を読み取ったうえで”、より効率的な表現を模索する。**

たとえば：

- BIOSレベルでのOSバイパス（GRUB再構成）  
- ファームウェア改変（ルータやゲーム機）  
- 閉鎖されたAPIの“暴露”と文書化（Hook技術など）

→ それは単なる“回避”ではなく、**“設計の盲点に気づく知性”の発露**である。

---

## 「コメントされないコード」を読む力

アセンブリは、コメントがない。関数名も変数名もない。  
すべてが匿名であり、意味は**命令列の構成と動き**からしか得られない。

だからこそ、ハッカーは：

- ジャンプ先を追い  
- スタック構造を解析し  
- レジスタの使い回しから**“書き手の意図”を再構築**する

→ これは言語分析ではない。**構造への共感と再表現**の行為である。

---

## 現代のハッカーとアセンブリの接続

- セキュリティ研究（バイナリ解析、ROP設計）  
- 自作OS・ブートローダ開発（UEFI, Coreboot）  
- FPGAやエミュレータの命令互換検証  
- AIモデル圧縮のハードウェア命令最適化

→ ハッカー精神は、**現代でも“構造の裏側にある透明な美しさ”を追求する場面**に必ず姿を現す。

---

## 結語：アセンブリとは、理解の最下層にある構造的詩である

アセンブリを読むとは、構造を“透かして見る”ことである。  
アセンブリを書くとは、意味を**物質的に構成する行為**である。  
ハッカーとは、それを**美学とする者たちの呼称**である。

> _"すべてのコードは読める。すべての構造は再構成できる。アセンブリはそのために存在し、ハッカーはそれを信じている。"_
デモシーンとサイズ最適化アセンブリ芸術



> _"1バイトを削るごとに、世界が洗練されていく。構造の詩とは、制限下で踊るロジックの舞である。"_  

デモシーン。  
それは、**制限の中で最大限の美を表現しようとするプログラマたちの創造運動**であり、  
アセンブリ言語は、その表現の極地で生まれた**最小単位の筆致**である。

本稿では、アセンブリによって紡がれる「サイズ最適化」という美学と、  
そこに宿る**計算と芸術、構造と創造、論理と詩の融合**を解剖していく。

---

## デモシーンとは何か？

1980年代、コンピュータが個人の手に届きはじめた時代。  
限られたメモリ・CPU・グラフィック性能の中で、**あえて最大の視覚表現・音楽表現を目指す者たち**が現れた。

- Commodore 64  
- Amiga 500  
- MS-DOS PC  
- Atari ST  

彼らは、**制限を破壊するのではなく、制限の中に可能性を見出した。**

そしてその多くは、**アセンブリで書かれていた。**

---

## サイズ制限という美学：256バイト／4KB／64KB

デモ作品には、しばしば**サイズ制限というルール**が課される。

| 制限サイズ | 特徴 |
|------------|------|
| 256B       | 命令1つ、1ピクセルすらが貴重な空間。純粋な構造美が試される。 |
| 4KB        | 簡易音楽、3D効果、インタラクションが成立する最小の舞台。 |
| 64KB       | 効果音、グラフィック、圧縮技術が融合し「製品」に近づく領域。 |

この構造において、**「機能を捨てる」のではなく「すべてを最小構文にまで還元する」**という思想が生まれる。

---

## アセンブリによる最適化：美のための削除技法

サイズ最適化は、次のような戦略で成り立つ。

### 1. 命令統合とループ再配置

```nasm
; 通常
mov al, 0
.loop:
  inc al
  cmp al, 100
  jne .loop

; 最適化（フラグ利用）
xor al, al
.loop:
  inc al
  jnz .loop
```

→ `cmp`命令を削除し、**フラグの残留効果を“意味として再解釈”**

---

### 2. データ→コードの吸収（ポリコード化）

```nasm
db 0xB8, 0x00, 0x00  ; mov ax, 0x0000
```

→ 意味を持たないデータですら、**命令列として構文化してしまう**。

---

### 3. サウンドや画像を式展開で生成

4KBデモでは、BGMさえも次のような式で構成される。

```c
sample = t*(t>>5|t>>8) & 0xFF;
```

→ テクスチャ、音波、演出すべてが**「数式」へと還元される構造的詩**。

---

## ファイルサイズの最小化技法

- 圧縮（UPX, kkrunchy）  
- セクション除去（.reloc や .rsrc）  
- Windows PE/ELF ヘッダの手動書き換え  
- スタブ化されたWinMain関数（Win32 4KB introなど）

→ 最終的には、**“OSの起動構造そのものを省略の対象とする”**という大胆な削減設計へと至る。

---

## 創造的制限：なぜ人は256バイトに魂を注ぐのか？

- **制約は自由を生む**：無限に使える構造では“構成の美”は現れにくい  
- **理解は完全になる**：256バイトすべての意味を開発者は理解し尽くす  
- **言語と設計の境界が溶ける**：命令そのものが構文的構造を持つ

→ サイズ最適化は、**“技術的競技”であると同時に、“構造に詩性を求める芸術運動”**である。

---

## 代表的な名作と文化的影響

- **"Elevated" (Farbrausch, 64KB)**：リアルタイム3Dと音楽の融合  
- **"256b intro: Tunnel"**：わずか数十命令で構成されるワープエフェクト  
- **"receptor" (4k)**：数学的オーディオビジュアル生成

これらの作品は、**視覚・聴覚・構造のすべてが“最小単位の記述”で支えられている**という点において共通する。

---

## 現代への接続：なぜいまもこの技法が尊ばれるのか？

- **教育的価値**：バイナリ、命令列、プロセッサ設計、OS構造をすべて実地で学べる  
- **エンジニアリング原理への回帰**：最小の構造で最大の効果を生む  
- **美学としての構造理解**：命令が意味を持ち、順番が詩になる世界観の体験

アセンブリは、最適化にとどまらず、**構造美とその語り方を教えてくれる言語**でもある。

---

## 結語：バイトは小さく、意味は深い

サイズ最適化されたアセンブリコードは、**限界の中に自由を見出す設計の最前線**である。

それは圧縮ではない。削除でもない。  
**意味を凝縮し、構造の核だけを残す美学の実装**である。

> _"1KBに満たないコードが、想像力の宇宙を展開する。アセンブリは、最小構文で最大表現を成し得る芸術言語なのだ。"_