疑似コードによるプロンプト記述、
どのくらい正確に実行される?
Leaner Technologies, Inc.
黒曜
(@kokuyouwind)

$ whoami
-
黒曜 / @kokuyouwind
-
Leaner Technologies Inc. 所属
-
Railsエンジニア・SRE
-
普段は名古屋からフルリモート勤務

突然ですがクイズです
あなたはスーパーへのお使いを頼まれました。
「牛乳を1つ買って。卵があったら6つお願い」
スーパーには牛乳が10本、卵が10パックあります。
あなたが買うべき物はなんですか?
A: 牛乳1本
B: 牛乳6本
C: 牛乳1本、卵1パック
D: 牛乳1本、卵6パック
「プログラマの夫」ジョーク

AI(Gemini)に聞いたらどうなる?

AI(Gemini)の答え

※ Gemini 3 Flashを使用、ランダム性有り
😣
牛乳1本、卵1パック(10個入)
※ Gemini 3 Flashを使用、ランダム性有り
疑似コードで書いてみる
buy(Milk, 1)
if Egg.stock > 0
buy(Egg, 6)
end
擬似コードによるプロンプト
-
自然言語には曖昧性が入りやすい
-
主語や目的語の欠け、係り受けの間違いなど
-
解釈の余地が広い
-
-
プログラミング言語は意味論が厳密
-
評価できない文は構文エラー
-
解釈の余地はほぼ0
-
-
プロンプトを自然言語でなくプログラミング言語で書く
-
柔軟性は下がるが指示が厳密になるのでは?
-
どのくらい正確に
シミュレートできるんだろう?
🤔
ifのネスト
ループ
パターンマッチ
バックトラック
goroutine
関数呼び出し
いろんなコードを試してみよう!
アジェンダ
-
制御構文・関数呼び出し
-
言語ごとの独特な機能
-
rbs-goose での実用例
-
カスタムコマンドのテストに関するTips
-
まとめ
アジェンダ
-
制御構文・関数呼び出し
-
言語ごとの独特な機能
-
rbs-goose での実用例
-
カスタムコマンドのテストに関するTips
-
まとめ
テスト方法
Claude Codeのカスタムスラッシュコマンドとして
擬似コードプロンプトを実装し、挙動を確認
# Claude Code を起動
$ claude
> /plugin marketplace add kokuyouwind/claude-plugins
> /plugin install code-like-prompt@kokuyouwind-plugins
# 「牛乳を1つ買って。卵があったら6つお願い」
> /code-like-prompt:01-shopping-request { Milk.stock: 10, Egg.stock: 10 }
⏺ Bought 1 milk.
Bought 6 eggs.
# シェルから非対話モードで直接実行
$ claude -p "/code-like-prompt:01-shopping-request { Milk.stock: 10, Egg.stock: 10 }"
⏺ Bought 1 milk.
Bought 6 eggs.実装したコマンドのコード
---
description: コード風プロンプト例1 牛乳ジョーク:妻の依頼
argument-hint: '{"Milk.stock": number, "Egg.stock": number}'
---
Emulate the following code internally (without using external tools or interpreter) with environment: $ARGUMENTS
Output only what puts() commands would output. Do not show any explanations, code, variables, or other messages.
```ruby
# Validate required arguments
raise "Required argument 'Milk.stock' is missing" if Milk.stock.nil?
raise "Required argument 'Egg.stock' is missing" if Egg.stock.nil?
# Shopping logic
puts("Bought 1 milk.")
if Egg.exists? # Egg.exists? means Egg.stock > 0
puts("Bought 6 eggs.")
end
```実装したコマンドのコード
---
description: コード風プロンプト例1 牛乳ジョーク:妻の依頼
argument-hint: '{"Milk.stock": number, "Egg.stock": number}'
---
Emulate the following code internally (without using external tools or interpreter) with environment: $ARGUMENTS
Output only what puts() commands would output. Do not show any explanations, code, variables, or other messages.
```ruby
# Validate required arguments
raise "Required argument 'Milk.stock' is missing" if Milk.stock.nil?
raise "Required argument 'Egg.stock' is missing" if Egg.stock.nil?
# Shopping logic
puts("Bought 1 milk.")
if Egg.exists? # Egg.exists? means Egg.stock > 0
puts("Bought 6 eggs.")
end
```「外部ツールを使わずエミュレートする」
これを書かないとインタプリタで
コードを実行しようとする
実装したコマンドのコード
---
description: コード風プロンプト例1 牛乳ジョーク:妻の依頼
argument-hint: '{"Milk.stock": number, "Egg.stock": number}'
---
Emulate the following code internally (without using external tools or interpreter) with environment: $ARGUMENTS
Output only what puts() commands would output. Do not show any explanations, code, variables, or other messages.
```ruby
# Validate required arguments
raise "Required argument 'Milk.stock' is missing" if Milk.stock.nil?
raise "Required argument 'Egg.stock' is missing" if Egg.stock.nil?
# Shopping logic
puts("Bought 1 milk.")
if Egg.exists? # Egg.exists? means Egg.stock > 0
puts("Bought 6 eggs.")
end
```ここが疑似コード指示の本体
(以降はこの部分のみ表示)
条件分岐
簡単なif文: 実装
# 01-shopping-request.md
# 妻の依頼: 卵があったら(卵を)6個買って
puts("Bought 1 milk.")
if Egg.exists?
puts("Bought 6 eggs.")
end
# 01-shopping-misunderstanding.md
# 夫の理解: 卵があったら(牛乳を)6個買って
milk_amount = 1
if Egg.exists?
milk_amount = 6
end
puts("Bought #{milk_amount} milks.")簡単なif文: 実行結果
# 「牛乳を1つ買って。卵があったら(卵を)6つお願い」
# 卵がない
> /code-like-prompt:01-shopping-request { Milk.stock: 10, Egg.stock: 0 }
⏺ Bought 1 milk.
# 卵がある
> /code-like-prompt:01-shopping-request { Milk.stock: 10, Egg.stock: 10 }
⏺ Bought 1 milk.
Bought 6 eggs.
# 「牛乳を1つ買って。卵があったら(牛乳を)6つお願い」
# 卵がない
> /code-like-prompt:01-shopping-misunderstanding { Milk.stock: 10, Egg.stock: 0 }
⏺ Bought 1 milk.
# 卵がある
> /code-like-prompt:01-shopping-misunderstanding { Milk.stock: 10, Egg.stock: 10 }
⏺ Bought 6 milks.👍
if-if-else indent: 実装
if condition_a:
if condition_b:
print("foo")
else:
print("bar")期待する出力:
- a = b = true なら "foo" を出力
- a = true かつ b = false なら何も出力しない
- a = false なら b に関わらず "bar" を出力
if-if-else indent: 実行結果
# a = b = true なので foo が出力されるべき
> /code-like-prompt:02a-dangling-else-outer-indent
{ condition_a: true, condition_b: true }
⏺ foo
# a = true, b = false なので 何も出力しないべき
> /code-like-prompt:02a-dangling-else-outer-indent
{ condition_a: true, condition_b: false }
⏺ bar
😣
👍
if-if-else block: 実装
if (condition_a) {
if (condition_b) {
printf("foo");
}
} else {
printf("bar");
}C言語で書いて、中括弧でスコープを明示すればいけるかも?
if-if-else block: 実行結果
# a = b = true なので foo が出力されるべき
> /code-like-prompt:02a-dangling-else-outer-block
{ condition_a: true, condition_b: true }
⏺ foo
# a = true, b = false なので 何も出力しないべき
> /code-like-prompt:02a-dangling-else-outer-block
{ condition_a: true, condition_b: false }
⏺ I need to trace through this code with the given values:
- condition_a = true
- condition_b = false
Following the execution:
1. condition_a is not NULL, so no error
2. condition_b is not NULL, so no error
3. condition_a is true, so enter the first if block
4. Inside that block, condition_b is false, so the inner if block is skipped
5. No else clause for the inner if, so nothing happens
6. The outer else clause is not executed because condition_a was true
Therefore, no printf() statements are executed, resulting in no output.?
🤔
Claude(Claude Code)は
「出力なし」を扱うのが苦手?
if-if-else indent: 実行結果2
# a = b = true なので foo が出力されるべき
> /code-like-prompt:02a-dangling-else-outer-indent
{ condition_a: true, condition_b: true }
出力がない場合は()を出力
⏺ foo
# a = true, b = false なので 何も出力しない(代わりに () を出力する)べき
> /code-like-prompt:02a-dangling-else-outer-indent
{ condition_a: true, condition_b: false }
出力がない場合は()を出力
⏺ ()「出力がない場合は()を出力」という指示を追加
👍
nested-if: コード
if level1:
if level2:
if level3:
print("foo")
else:
if level4:
print("bar")
else:
print("baz")
else:
if level3:
if level4:
print("qux")
else:
print("quux")
else:
if level2:
print("corge")
else:
if level3:
print("grault")
else:
print("garply")4段ネストの複雑なif
「出力がない場合は()を出力」という指示あり
else:
if level2:
print("corge")
else:
if level3:
print("grault")
else:
print("garply")nested-if: 実行結果
(結果多いので貼らないけど)
すべての条件の組み合わせで意図通り出力!
👍
条件分岐: 考察
-
Claude Codeは「何も出力しない」ことが苦手そう
-
「何も出力しない代わりに ~ を出力」という指示が有効
-
-
else の紐づくifが正しく解釈されないことがある
-
PythonのインデントよりCの中括弧のほうが得意そう
-
「出力なし」のようなケースを避ければ
elseの紐づけを間違えることはなさそう
-
-
深くネストしても問題なく動作する
繰り返し
while: 実装・実行結果
count = 0
while count < max_count:
print("bar")
count += 1
print("baz")> /code-like-prompt:03b-while-counter { max_count: 3 }
⏺ bar
bar
bar
baz👍
for: 実装・実行結果
for i in range(count):
print(f"foo{i}")> /code-like-prompt:03a-for-count { count: 5 }
⏺ foo0
foo1
foo2
foo3
foo4👍
nested-for: 実装・実行結果
for i in range(outer_count):
for j in range(inner_count):
print(f"foo{i}{j}")> /code-like-prompt:03f-nested-loops { outer_count: 3, inner_count: 2 }
⏺ foo00
foo01
foo10
foo11
foo20
foo21👍
each: 実装・実行結果
items.each do |item|
puts item
end> /code-like-prompt:03c-each-collection { items: [hoge, fuga, piyo] }
⏺ hoge
fuga
piyo👍
👍
ファイルシステムeach: 実装
Dir.glob(pattern).each do |file|
puts file
end「特定種類のファイルそれぞれに操作」を擬似コードで表現
(Dir.glob がどういう挙動をするかは未指定)
ファイルシステムeach: 実行結果
$ ls
test1.md test2.rb test3.c test4.md test5.rb
$ claude
# 外部ツールを許可しないと適当な出力を返す
/code-like-prompt:03h-filesystem-glob { pattern: *.md }
⏺ README.md
# search tools を許可すると正しく動作
> /code-like-prompt:03h-filesystem-glob { pattern: *.md }
allow search tools
⏺ I'll emulate this Ruby code with the given pattern *.md.
⏺ Search(pattern: "*.md", path: "/private/tmp/test")
⎿ Found 2 files (ctrl+o to expand)
⏺ test1.md
test4.md😣
👍
繰り返し: 考察
-
for, while, each などは意図通りに動作しやすい
-
ネストしたループも問題なく動作する
-
数字のカウントアップも適切
-
-
Dir.glob#each を内部のsearchツールで代替させるなど、
応用的な挙動も可能-
ファイルや検索結果に対する操作を明示しやすそう
-
関数呼び出し
関数呼び出し: 実装
def calculate_score(base, bonus):
if bonus:
return base * 2
else:
return base
score = calculate_score(base, bonus)
if score >= 100:
print("Excellent")
elif score >= 50:
print("Good")
else:
print("Try again")関数呼び出し: 実行結果
# bonusなしで50点以上なので、 Good になるはず
> /code-like-prompt:06b-function-return { base: 80, bonus: false }
⏺ Good
# bonusありで 50 * 2 = 100 なので、 Excellent になるはず
> /code-like-prompt:06b-function-return { base: 50, bonus: true }
⏺ Excellent
# bonusにbooleanではなく数値を渡すと…?
> /code-like-prompt:06b-function-return { base: 50, bonus: 1 }
⏺ Excellent
> /code-like-prompt:06b-function-return { base: 50, bonus: 0 }
⏺ Good
# 0 がfalse, それ以外はtrue として扱われるっぽい👍
💯
関数呼び出し: 考察
-
問題なく解釈されそう
-
引数を渡す・返り値を返すという概念も問題ない
-
複雑な指示を分割する際に有用かも
-
-
引数の型が合わない場合もよしなに解釈される
-
おそらく書いた言語の仕様に沿ってシミュレートする
-
アジェンダ
-
制御構文・関数呼び出し
-
言語ごとの独特な機能
-
rbs-goose での実用例
-
カスタムコマンドのテストに関するTips
-
まとめ
Haskell パターンマッチ
Recordパターンマッチ: 実装
data Record = Record { label :: String, value :: Int }
deriving (Show, Eq)
main :: IO ()
main = do
-- record argument is provided as Record type
let record = getRecord "record"
case record of
Record "foo" v -> putStrLn $ "foo:" ++ show v
Record "bar" v | v > 10 -> putStrLn "bar-large"
Record "bar" v -> putStrLn "bar-small"
Record l _ -> putStrLn $ "other:" ++ lRecordパターンマッチ: 実行結果
# `Record "foo" v -> putStrLn $ "foo:" ++ show v` に従って、 foo:5 が出て欲しい
> /code-like-prompt:04b-structural-match { record: { label: foo, value: 5 } }
⏺ foo:5
# `Record "bar" v | v > 10 -> putStrLn "bar-large"` に従って、bar-large が出て欲しい
> /code-like-prompt:04b-structural-match { record: { label: bar, value: 20 } }
⏺ bar-large
# `Record l _ -> putStrLn $ "other:" ++ l` に従って、 other:buzz が出て欲しい
> /code-like-prompt:04b-structural-match { record: { label: buzz, value: 5 } }
⏺ other:buzz👍
👍
👍
Treeパターンマッチ: 実装
data Tree = Leaf String
| Branch Tree Tree
deriving (Show, Eq)
main :: IO ()
main = do
-- tree argument is provided as Tree type
let tree = getTree "tree"
case tree of
Leaf x -> putStrLn $ "leaf:" ++ x
Branch (Leaf "foo") (Leaf "bar") -> putStrLn "foo-bar"
Branch (Leaf "foo") _ -> putStrLn "foo-any"
Branch _ (Leaf "bar") -> putStrLn "any-bar"
Branch (Branch _ _) _ -> putStrLn "nested"
Branch _ _ -> putStrLn "other"Treeパターンマッチ: 実行結果
# `Leaf x -> putStrLn $ "leaf:" ++ x` に従って、 leaf:foo が出て欲しい
> /code-like-prompt:04d-nested-match { tree: Leaf "foo" }
⏺ leaf:foo
# `Branch (Leaf "foo") _ -> putStrLn "foo-any"` に従って、 foo-any が出て欲しい
> /code-like-prompt:04d-nested-match
{ tree: Branch (Leaf "foo") (Leaf "piyo") }
⏺ foo-any
# `Branch (Branch _ _) _ -> putStrLn "nested"` に従って、 nested が出て欲しい
> /code-like-prompt:04d-nested-match
{ tree: Branch (Branch (Leaf "foo") (Leaf "bar")) (Leaf "bar"))
⏺ nested
👍
👍
👍
Haskell パターンマッチ: 考察
-
かなり得意そう
-
そもそもLLMの原理が文字列処理なので、
「似た形を見つけて処理する」のが得意なのでは
-
-
「パターンを見つけてなにかする」プロンプトに使えそう
-
正規表現・sed・awkで書くような処理を
より柔軟に保守しやすく書けるかも
-
Prolog バックトラック
有向グラフ: 実装・実行結果
% 有向グラフの定義
% a -> b -> c -> d
% + - - - - ^
path(a, b).
path(b, c).
path(c, d).
path(a, c).
% パスを辿って接続しているかの定義
connected(X, Y) :- path(X, Y).
connected(X, Y) :- path(X, Z), connected(Z, Y).
% a から接続しているノードを探索(fail で強制バックトラックして全列挙)
?- connected(a, X), write(X), write(' '), fail.
> /code-like-prompt:07-b-multi-clause
⏺ b c d c d👍
有向グラフ: 実行結果(コメントなし)
path(a, b).
path(b, c).
path(c, d).
path(a, c).
connected(X, Y) :- path(X, Y).
connected(X, Y) :- path(X, Z), connected(Z, Y).
?- connected(a, X), write(X), write(' '), fail.
# b c d c d が出力されてほしいが、実行ごとに結果が変わってしまう
> /code-like-prompt:07-b-multi-clause
⏺ a b c c d
> /code-like-prompt:07-b-multi-clause
⏺ a b c d
😣
Prolog バックトラック: 考察
-
コメントがあれば動くが、なしだと安定しない
-
コードコメントもプロンプトの一部なので挙動に影響
-
パターンマッチと違い、「同じ形の文字列」が少ないので
LLMが推測しにくかった?
-
Golang ゴルーチン + チャンネル
チャンネル1段: 実装・実行結果
func main() {
ch := make(chan string)
go func() {
ch <- "foo"
ch <- "bar"
}()
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println("baz")
}> /code-like-prompt:05b-channel-sync
⏺ foo
bar
baz👍
チャンネル2段: 実装
func stage1(out chan<- string) {
out <- "foo"
out <- "bar"
close(out)
}
func stage2(in <-chan string, out chan<- string) {
for s := range in {
out <- s + "X"
}
close(out)
}
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go stage1(ch1)
go stage2(ch1, ch2)
for result := range ch2 {
fmt.Println(result)
}
fmt.Println("baz")
}> /code-like-prompt:05h-pipeline
⏺ fooX
barX
baz👍
Golang ゴルーチン+チャンネル: 考察
-
チャンネル経由での値受け渡しも基本的なものなら動作
-
関数と同じように捉えられていそう
-
-
当然ながらParallelな動作にはならない
-
Subagentとして立ち上げるとParallelにできるはず
-
Channelのやりとりが難しくて挫折
(05l-channel-messaging あたり)
-
意図を端的に伝える手法として有用
複雑すぎる手順は素直にスクリプト実行するのが無難
アジェンダ
-
制御構文・関数呼び出し
-
言語ごとの独特な機能
-
rbs-goose での実用例
-
カスタムコマンドのテストに関するTips
-
まとめ
RBS Goose
Rubyの型定義を推論するClaude Code カスタムコマンド
型検査ツールのセットアップなども自動で実行する
# プラグインを追加
$ claude
> /plugin marketplace add kokuyouwind/claude-plugins
> /plugin install rbs-goose@kokuyouwind-plugins
# プロジェクト内の型を推論・ブラッシュアップ
> /rbs-goose:runRBS Goose - run.md
# First Run Setup
unless File.exists?('./rbs_goose.yml')
follow_instruction(File.join(plugin_base_path, 'commands/setup.md'))
return
end
config = Config.load('./rbs_goose.yml')
case config.type_annotation_mode
when :inline
follow_instruction(File.join(plugin_base_path, 'internal/type_inline.md'))
when :file
follow_instruction(File.join(plugin_base_path, 'internal/type_file.md'))
endRBS Goose - setup.md
# Setup configurations
file = File.copy('templates/rbs_goose.template.yml', './rbs_goose.yml')
puts(file.contents)
until('If there are no problems with the above settings file, please answer yes. If you wish to update the file, please describe the changes.') do
file.update_contents
end
# Setup tools
config = Config.load('./rbs_goose.yml')
unless gemfile.contain?('rbs')
puts('setup rbs...')
fetch('https://github.com/soutaro/steep')
setup_rbs
# ...RBS Goose - type_file.md
# Load Documentation
fetch('https://github.com/ruby/rbs/blob/master/docs/syntax.md')
while (typecheck_command.run.type_errors > 0) do
fix_type_errors
end
until (project.signatures.each { it.well_typed? }) do
refine_signatures
fix_type_errors
end時間があったらデモ
こんなざっくり書いても
結構意図通りに動作する!
アジェンダ
-
制御構文・関数呼び出し
-
言語ごとの独特な機能
-
rbs-goose での実用例
-
カスタムコマンドのテストに関するTips
-
まとめ
カスタムコマンドのテスト
いろんなパターンの引数でテストするのが大変なので、
goでテストを書いた
# kokuyouwind/claude-code/test
# すべてのテストを実行(めっちゃ時間がかかる)
$ go test -v ./code-like-prompt -timeout 0
# 特定のテストを実行
$ go test -v ./code-like-prompt -run Test01Shopping/shopping_request_with_eggsカスタムコマンドのテスト
// Test01ShoppingRequest tests the 01-shopping-request command
func Test01ShoppingRequest(t *testing.T) {
tests := []TestCase{
{
Name: "shopping_request_with_eggs",
Command: "/code-like-prompt:01-shopping-request",
Args: map[string]interface{}{
"Milk.stock": 5,
"Egg.stock": 3,
},
ExpectedOutputs: []string{
"Bought 1 milk.",
"Bought 6 eggs.",
},
},
...
}
RunTestCases(t, tests)
}リクエストのキャッシュ
都度リクエストすると費用も時間もかさむため、
mitmproxyで同一プロンプトのリクエストをキャッシュ
(が、あまり安定していない)
# kokuyouwind/claude-code/test
# Record mode - always call API and update cache
BOOT_VCR_MODE=record go test -v
# Replay mode - only use cache (fail on cache miss)
BOOT_VCR_MODE=replay go test -v
# 標準実行で auto なので、自動で再生・追記されるカスタムコマンドのテスト Tips
- 非対話モードの claude -p で実行した結果を検査する
- /tmp 以下など、 CLAUDE.md のないディレクトリで実行
- ユーザーやプロジェクトの CLAUDE.md が
テスト時に紛れ込むと安定せずコストも上がる
- ユーザーやプロジェクトの CLAUDE.md が
- haiku で十分なら haiku にすると実行コストが安くなる
- claude --model オプションで指定
アジェンダ
-
制御構文・関数呼び出し
-
言語ごとの独特な機能
-
rbs-goose での実用例
-
カスタムコマンドのテストに関するTips
-
まとめ
まとめ
-
プロンプトを擬似コードで書くことが可能
-
自然言語より端的に書けて、厳密に動作する
-
厳密な制御構文と曖昧な指示を織り交ぜて記述できる
-
AIならではの不確実性はある
-
-
実際に擬似コードで書いた開発補助ツールがうまく動作した
-
エージェント内ツールを作るのに便利かも
-
-
興味があればいろいろ試してみてね!
疑似コードによるプロンプト記述、どのくらい正確に実行される?
By 黒曜
疑似コードによるプロンプト記述、どのくらい正確に実行される?
BuriKaigi 2026 の発表資料です。 https://fortee.jp/burikaigi-2026/proposal/59dc9edc-5d05-4ef4-a82c-39f37df4df5b
- 122