Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Rubyでつくる
型付きRuby
とちぎRuby会議07 (2017-08-26)
@mametter(遠藤侑介)
自己紹介:@mametter(遠藤侑介)
• Ruby コミッタ(2008年~)
• Ruby への主な貢献
– Ruby 本体のテストや
RubySpec を増強した
– カバレッジ測定機能を実装した
• Ruby 2.5で分岐カバレッジ実装...
自己紹介:@mametter(遠藤侑介)
• いろいろ本を書いてます(翻訳を含む)
Quine
とか変なプログラム
の本
インタプリタ
をRubyで
自作してみる本
型システム
の教科書
(”TAPL”の翻訳)
代入禁止
プログラミングの本
(...
今日のテーマ
• Ruby でつくる Ruby
Quine
とか変なプログラム
の本
インタプリタ
をRubyで
自作してみる本
型システム
の教科書
(”TAPL”の翻訳)
代入禁止
プログラミングの本
(”PFDS”の翻訳)
型システム
の...
Ruby インタプリタって何?
(matz製の)
Rubyインタプリタ
みなさんに質問
Rubyインタプリタ
作ったことのある人?
もしここが「とちぎScheme会議」だったら
• たぶんこうなる
みんな Ruby インタプリタ書こう!
• プログラマの教養として、自分の好きな言語の
インタプリタくらい作っておきたい!
– 実用レベルじゃなくてよい
– SchemeやMLの入門では定番の教材
• ガイドブックを書いた
– 144 ページ、...
目次
• 『Ruby でつくる Ruby』ダイジェスト
– 概要  今からここ
– インタプリタの構成と実装例
– ゴールと意義
• Ruby でつくる型付き Ruby
– Ruby と型の概観
– 漸進的型付けのアイデアと実装例
• まとめ
プログラミング言語とは
• コンピュータに対する指示を書くための言葉
コンピュータは
Ruby プログラムを
直接理解できない
✕
インタプリタとは
• プログラムの指示をコンピュータに伝えるプログ
ラム
– interpreter: 通訳
インタプリタは
コンピュータが
理解できるように
書いておく
インタプリタは
プログラムを読んで
解釈し、実行する
本書の内容
• Ruby 言語で Ruby インタプリタを書いてみる
– 「Ruby プログラムを読んで、解釈し、実行する」
– という Ruby プログラムを書く
• (eval は使わない)
• 疑問:作ったインタプリタはどうやって動かす?...
つまり、こういうこと
※こんなものを作って
何の意味があるかは後述
実際に作るもの
• MinRuby:Ruby のコア部分を切り出した言語
– 演算式(1 + 2 とか)
– 変数(x = 1 とか p(x) とか)
– 分岐とループ(if と while)
– 関数、配列とハッシュ
– オブジェクト指向やブ...
目次
• 『Ruby でつくる Ruby』ダイジェスト
– 概要
– インタプリタの構成と実装例  今からここ
– ゴールと意義
• Ruby でつくる型付き Ruby
– Ruby と型の概観
– 漸進的型付けのアイデアと実装例
• まとめ
?
インタプリタの構成
入力
MinRuby
プログラムの
テキストファイル
出力
プログラムの
実行結果
1+2*3
7
構文解析
計算の木
(抽象構文木)
評価
プログラム文字列を
「木」に変換する
(ライブラリ使用)
「木」を見て
計算を...
Ruby での木の表現方法
• ["演算子", 左の枝, 右の枝]
という配列を入れ子にして表現する
["+",
1,
["*",
2,
3
]
]
Ruby での木の表現方法
• ["演算子", 左の枝, 右の枝]
という配列を入れ子にして表現する
["+",
1,
["*",
2,
3
]
]
["+",
["lit", 1],
["*",
["lit", 2],
["lit", 3],...
評価のイメージ
• 枝を切って
葉に差し替える
• これを繰り返す
• 葉だけに
なったら終了
評価器の実装
• 関数の再帰呼び出しを使う
– 本書における「関数」の定義=木を辿るための道具
再帰の初心者向け解説は、フィボナッチより木のトラバースの方がわかり
やすいのではないだろうか(木という目に見える再帰構造があるので)
def eva...
インタプリタ実装の進め方
1. 実装する言語機能の抽象構文木を観察する
2. 解釈の方法をプログラムに落としていく
• 例:if 文
– 抽象構文木:[“if”, 条件式, then部分, else部分]
– インタプリタ実装:
– if 文の...
つづきは本書で
• 言語機能をガンガン足していく
– 5章:複文、変数代入・参照
– 6章:if 文、while 文
– 7、8章:関数呼び出し、関数定義
– 9章:配列やハッシュの作成・参照・代入
MinRuby インタプリタの出来上がり
...
MinRuby インタプリタ全貌
require "minruby"
def evaluate(exp, genv, lenv)
case exp[0]
when "stmts"
last = nil
i = 1
while exp[i]
la...
目次
• 『Ruby でつくる Ruby』ダイジェスト
– 概要
– インタプリタの構成と実装例
– ゴールと意義  今からここ
• Ruby でつくる型付き Ruby
– Ruby と型の概観
– 漸進的型付けのアイデアと実装例
• まとめ
できたもの
• MinRuby で書かれた MinRuby インタプリタ
 インタプリタ自身も MinRuby プログラム
インタプリタがインタプリタ自身を実行できる!
(ブートストラップ)
本書のゴール:ブートストラップ
• 自分で書いたインタプリタで
• 自分で書いた
インタプリタ を
動かす
※インタプリタで
ブートストラップとは
言わないかも
RubyでRubyインタプリタを書く意義
• 実用上は無意味
– 機械語で書かれていないインタプリタは役に立たない
– 世の中、意味のないことのほうが面白い
• インタプリタの原理と構成を理解できる
– 教科書的な知識だけでなく実際に手を動かす...
目次
• 『Ruby でつくる Ruby』ダイジェスト
– 概要
– インタプリタの構成と実装例
– ゴールと意義
• Ruby でつくる型付き Ruby
– Ruby と型の概観  今からここ
– 漸進的型付けのアイデアと実装例
• まとめ
型とは
• 変数の取りうる値の範囲を表現したもの
– この呼び出しは OK
– この呼び出しは NG
• 参考情報:Matz は Ruby 3 で型を入れたいと言っている
– RubyKaigi 2017 @ 広島 でも関連発表が複数ありそ...
型の目的
• 関数引数の想定を簡潔にドキュメント化したい
– うざい
• テストなしで自動的に『バグ』を見つけたい
– テストで 1 時間待ちたくない
sleep 3600
add("foo", 2)
# 引数 x と y は Intege...
型で見つけたい『バグ』とは?
• この関数に対しては
– これにバグはない
– ほぼ明らかにバグ
– 動くけど目的外利用
def add_int(x, y)
x + y
end
add_int(1, 2)
add_int("foo", 2...
型で見つけたい『バグ』とは?
• この関数に対しては
– これにバグはない
– ほぼ明らかにバグ
– OKかも?(ダックタイピング)
– バグかどうかはプログラム単体では一意に決まらない
– プログラマの意図に依存する
def add(x...
ダックタイピングを許す型注釈の例
• 解決1:オーバーロード
– 許したい型が増えたらドンドンうざくなる
• 解決2:構造的型付け
– 型が長く難しくなりがち
• 解決3:漸進的型付け
def add(x:Integer, y:Integer)...
型のその他の問題
• 型注釈を書くのが面倒
• 型注釈のない既存資産が大量に存在する
– これらもだいたい漸進的型付けで解決できる
目次
• 『Ruby でつくる Ruby』ダイジェスト
– 概要
– インタプリタの構成と実装例
– ゴールと意義
• Ruby でつくる型付き Ruby
– Ruby と型の概観
– 漸進的型付けのアイデアと実装例  今からここ
• まとめ
漸進的型付け [Siek ‘06] のアイデア
• 型ありと型なしの同居を認める
– 型注釈があったら型チェックされる
– 型注釈がなかったら型チェックしない
• Ruby での使い方
– ダックタイピングしたいところには型注釈を書かない
– ...
漸進的型付けの実装イメージ
• 型注釈がない変数は any 型とする
• any 型が絡む演算の結果は any 型とする
– すごく単純なアイデアだけど
2006 年頃にようやく登場
• 部分型(継承関係)を使った
アプローチが研究の泥沼だった...
実装してみた、デモ
def add(x, y)
x + y
end
def add_int(x:Integer, y:Integer)
x + y
end
p(add(1, 2)) #=> 3
p(add("foo", 2)) #=> 実行時例...
まとめ:型付き MinRuby
• MinRuby を実験用環境として使う
– クラスやブロックなど、難しいものがないので実験し
やすい
• 漸進的型付けの Ruby 実用化への課題
– クラスやブロックなど、難しいものにも対応しないと
いけな...
目次
• 『Ruby でつくる Ruby』ダイジェスト
– 概要
– インタプリタの構成と実装例
– ゴールと意義
• Ruby でつくる型付き Ruby
– Ruby と型の概観
– 漸進的型付けのアイデアと実装例
• まとめ  今からここ
まとめ
• 『Ruby でつくる Ruby』
– Ruby 言語で
Ruby インタプリタを作る本
• この会場で直販中!
• インタプリタの原理と構成を
学べる
• Ruby 言語を冷静に見つめ、
機能拡張とかを考えられる
– 型とか
Upcoming SlideShare
Loading in …5
×

Ruby でつくる型付き Ruby

1,340 views

Published on

とちぎ Ruby 会議 07

Published in: Technology
  • Be the first to comment

Ruby でつくる型付き Ruby

  1. 1. Rubyでつくる 型付きRuby とちぎRuby会議07 (2017-08-26) @mametter(遠藤侑介)
  2. 2. 自己紹介:@mametter(遠藤侑介) • Ruby コミッタ(2008年~) • Ruby への主な貢献 – Ruby 本体のテストや RubySpec を増強した – カバレッジ測定機能を実装した • Ruby 2.5で分岐カバレッジ実装予定! くわしくは RubyKaigi 2017@広島(9/18~20)で – リリース管理に関わった(特に Ruby 1.9.2 と 2.0) – キーワード引数やデッドロック検出などを実装した ’06下 ’07上 ’07下 ’08上 60 70 80 90 100 coverage(%) 70% 85% C0カバレッジ遷移
  3. 3. 自己紹介:@mametter(遠藤侑介) • いろいろ本を書いてます(翻訳を含む) Quine とか変なプログラム の本 インタプリタ をRubyで 自作してみる本 型システム の教科書 (”TAPL”の翻訳) 代入禁止 プログラミングの本 (”PFDS”の翻訳) ※翻訳歓迎!
  4. 4. 今日のテーマ • Ruby でつくる Ruby Quine とか変なプログラム の本 インタプリタ をRubyで 自作してみる本 型システム の教科書 (”TAPL”の翻訳) 代入禁止 プログラミングの本 (”PFDS”の翻訳) 型システム の教科書 (”TAPL”の翻訳) 型付き Ruby
  5. 5. Ruby インタプリタって何? (matz製の) Rubyインタプリタ
  6. 6. みなさんに質問 Rubyインタプリタ 作ったことのある人?
  7. 7. もしここが「とちぎScheme会議」だったら • たぶんこうなる
  8. 8. みんな Ruby インタプリタ書こう! • プログラマの教養として、自分の好きな言語の インタプリタくらい作っておきたい! – 実用レベルじゃなくてよい – SchemeやMLの入門では定番の教材 • ガイドブックを書いた – 144 ページ、128 行で インタプリタが書ける! – 対象読者:プログラミング未経験者 から Ruby 経験者まで
  9. 9. 目次 • 『Ruby でつくる Ruby』ダイジェスト – 概要  今からここ – インタプリタの構成と実装例 – ゴールと意義 • Ruby でつくる型付き Ruby – Ruby と型の概観 – 漸進的型付けのアイデアと実装例 • まとめ
  10. 10. プログラミング言語とは • コンピュータに対する指示を書くための言葉 コンピュータは Ruby プログラムを 直接理解できない ✕
  11. 11. インタプリタとは • プログラムの指示をコンピュータに伝えるプログ ラム – interpreter: 通訳 インタプリタは コンピュータが 理解できるように 書いておく インタプリタは プログラムを読んで 解釈し、実行する
  12. 12. 本書の内容 • Ruby 言語で Ruby インタプリタを書いてみる – 「Ruby プログラムを読んで、解釈し、実行する」 – という Ruby プログラムを書く • (eval は使わない) • 疑問:作ったインタプリタはどうやって動かす? – 答え:Matz 製のインタプリタに動かしてもらう
  13. 13. つまり、こういうこと ※こんなものを作って 何の意味があるかは後述
  14. 14. 実際に作るもの • MinRuby:Ruby のコア部分を切り出した言語 – 演算式(1 + 2 とか) – 変数(x = 1 とか p(x) とか) – 分岐とループ(if と while) – 関数、配列とハッシュ – オブジェクト指向やブロックは不必要(!)なので省く • 本書の正確な内容:MinRuby で MinRuby インタ プリタを作る – インタプリタの実装も楽になる – 覚えることが少ない(プログラミング未経験者でも読める!)
  15. 15. 目次 • 『Ruby でつくる Ruby』ダイジェスト – 概要 – インタプリタの構成と実装例  今からここ – ゴールと意義 • Ruby でつくる型付き Ruby – Ruby と型の概観 – 漸進的型付けのアイデアと実装例 • まとめ
  16. 16. ? インタプリタの構成 入力 MinRuby プログラムの テキストファイル 出力 プログラムの 実行結果 1+2*3 7 構文解析 計算の木 (抽象構文木) 評価 プログラム文字列を 「木」に変換する (ライブラリ使用) 「木」を見て 計算を行う
  17. 17. Ruby での木の表現方法 • ["演算子", 左の枝, 右の枝] という配列を入れ子にして表現する ["+", 1, ["*", 2, 3 ] ]
  18. 18. Ruby での木の表現方法 • ["演算子", 左の枝, 右の枝] という配列を入れ子にして表現する ["+", 1, ["*", 2, 3 ] ] ["+", ["lit", 1], ["*", ["lit", 2], ["lit", 3], ] ]
  19. 19. 評価のイメージ • 枝を切って 葉に差し替える • これを繰り返す • 葉だけに なったら終了
  20. 20. 評価器の実装 • 関数の再帰呼び出しを使う – 本書における「関数」の定義=木を辿るための道具 再帰の初心者向け解説は、フィボナッチより木のトラバースの方がわかり やすいのではないだろうか(木という目に見える再帰構造があるので) def evaluate(tree) if tree[0] == "lit" tree[1] elsif tree[0] == "+" evaluate(tree[1]) + evaluate(tree[2]) elsif tree[0] == "*" evaluate(tree[1]) * evaluate(tree[2]) end end “演算子”の部分 左の枝 右の枝
  21. 21. インタプリタ実装の進め方 1. 実装する言語機能の抽象構文木を観察する 2. 解釈の方法をプログラムに落としていく • 例:if 文 – 抽象構文木:[“if”, 条件式, then部分, else部分] – インタプリタ実装: – if 文の実装に if を使う インタプリタの本質は ターゲット言語からホスト言語への丸投げ if evaluate(tree[1]) evaluate(tree[2]) else evaluate(tree[3]) end
  22. 22. つづきは本書で • 言語機能をガンガン足していく – 5章:複文、変数代入・参照 – 6章:if 文、while 文 – 7、8章:関数呼び出し、関数定義 – 9章:配列やハッシュの作成・参照・代入 MinRuby インタプリタの出来上がり – 執筆時に工夫したこと • 各章で一旦完結させる – 動作しない状態で 終わらない – 演習問題を入れる • 退屈にならないようにする – 実装対象の言語機能を考察する – 環境などの必要な拡張をしたり – each とか使ってない (ブロックがないので使えない)
  23. 23. MinRuby インタプリタ全貌 require "minruby" def evaluate(exp, genv, lenv) case exp[0] when "stmts" last = nil i = 1 while exp[i] last = evaluate(exp[i], genv, lenv) i = i + 1 end last when "lit" exp[1] when "+" evaluate(exp[1], genv, lenv) + evaluate(exp[2], genv, lenv) when "-" evaluate(exp[1], genv, lenv) - evaluate(exp[2], genv, lenv) when "*" evaluate(exp[1], genv, lenv) * evaluate(exp[2], genv, lenv) when "/" evaluate(exp[1], genv, lenv) / evaluate(exp[2], genv, lenv) when "%" evaluate(exp[1], genv, lenv) % evaluate(exp[2], genv, lenv) when "==" evaluate(exp[1], genv, lenv) == evaluate(exp[2], genv, lenv) when "<" evaluate(exp[1], genv, lenv) < evaluate(exp[2], genv, lenv) when "<=" evaluate(exp[1], genv, lenv) <= evaluate(exp[2], genv, lenv) when ">" evaluate(exp[1], genv, lenv) > evaluate(exp[2], genv, lenv) when ">=" evaluate(exp[1], genv, lenv) >= evaluate(exp[2], genv, lenv) when "var_ref" lenv[exp[1]] when "var_assign" lenv[exp[1]] = evaluate(exp[2], genv, lenv) when "if" if evaluate(exp[1], genv, lenv) evaluate(exp[2], genv, lenv) else evaluate(exp[3], genv, lenv) if exp[3] end when "while" while evaluate(exp[1], genv, lenv) evaluate(exp[2], genv, lenv) end when "func_def" genv[exp[1]] = ["user_defined", exp[2], exp[3]] when "func_call" args = [] i = 0 while exp[i + 2] args[i] = evaluate(exp[i + 2], genv, lenv) i = i + 1 end mhd = genv[exp[1]] if mhd[0] == "builtin" minruby_call(mhd[1], args) else new_lenv = {} params = mhd[1] i = 0 while params[i] new_lenv[params[i]] = args[i] i = i + 1 end evaluate(mhd[2], genv, new_lenv) end when "ary_new" ary = [] i = 0 while exp[i + 1] ary[i] = evaluate(exp[i + 1], genv, lenv) i = i + 1 end ary when "ary_assign" ary = evaluate(exp[1], genv, lenv) idx = evaluate(exp[2], genv, lenv) val = evaluate(exp[3], genv, lenv) ary[idx] = val when "ary_ref" ary = evaluate(exp[1], genv, lenv) idx = evaluate(exp[2], genv, lenv) ary[idx] when "hash_new" hsh = {} i = 0 while exp[i + 1] key = evaluate(exp[i + 1], genv, lenv) val = evaluate(exp[i + 2], genv, lenv) hsh[key] = val i = i + 2 end hsh else p("error") pp(exp) raise "unknown node: #{ exp[0] }" end end genv = { "p" => ["builtin", "p"], "require" => ["builtin", "require"], "minruby_parse" => ["builtin", "minruby_parse"], "minruby_load" => ["builtin", "minruby_load"], "minruby_call" => ["builtin", "minruby_call"], } lenv = {} evaluate(minruby_parse(minruby_load()), genv, lenv) 空行含めて 無理なく 128 行
  24. 24. 目次 • 『Ruby でつくる Ruby』ダイジェスト – 概要 – インタプリタの構成と実装例 – ゴールと意義  今からここ • Ruby でつくる型付き Ruby – Ruby と型の概観 – 漸進的型付けのアイデアと実装例 • まとめ
  25. 25. できたもの • MinRuby で書かれた MinRuby インタプリタ  インタプリタ自身も MinRuby プログラム インタプリタがインタプリタ自身を実行できる! (ブートストラップ)
  26. 26. 本書のゴール:ブートストラップ • 自分で書いたインタプリタで • 自分で書いた インタプリタ を 動かす ※インタプリタで ブートストラップとは 言わないかも
  27. 27. RubyでRubyインタプリタを書く意義 • 実用上は無意味 – 機械語で書かれていないインタプリタは役に立たない – 世の中、意味のないことのほうが面白い • インタプリタの原理と構成を理解できる – 教科書的な知識だけでなく実際に手を動かすのは重要 – ブートストラップに成功したときの達成感は半端ない • Ruby 言語自体を深く考察する土台になる – たとえば型とか
  28. 28. 目次 • 『Ruby でつくる Ruby』ダイジェスト – 概要 – インタプリタの構成と実装例 – ゴールと意義 • Ruby でつくる型付き Ruby – Ruby と型の概観  今からここ – 漸進的型付けのアイデアと実装例 • まとめ
  29. 29. 型とは • 変数の取りうる値の範囲を表現したもの – この呼び出しは OK – この呼び出しは NG • 参考情報:Matz は Ruby 3 で型を入れたいと言っている – RubyKaigi 2017 @ 広島 でも関連発表が複数ありそう def add_int(x: Integer, y: Integer) x + y end 型注釈 add_int(1, 2) add_int("foo", 2)
  30. 30. 型の目的 • 関数引数の想定を簡潔にドキュメント化したい – うざい • テストなしで自動的に『バグ』を見つけたい – テストで 1 時間待ちたくない sleep 3600 add("foo", 2) # 引数 x と y は Integer を受け付けます def add(x, y) x + y end
  31. 31. 型で見つけたい『バグ』とは? • この関数に対しては – これにバグはない – ほぼ明らかにバグ – 動くけど目的外利用 def add_int(x, y) x + y end add_int(1, 2) add_int("foo", 2) add_int("foo", "bar")
  32. 32. 型で見つけたい『バグ』とは? • この関数に対しては – これにバグはない – ほぼ明らかにバグ – OKかも?(ダックタイピング) – バグかどうかはプログラム単体では一意に決まらない – プログラマの意図に依存する def add(x, y) x + y end add(1, 2) add("foo", 2) add("foo", "bar")
  33. 33. ダックタイピングを許す型注釈の例 • 解決1:オーバーロード – 許したい型が増えたらドンドンうざくなる • 解決2:構造的型付け – 型が長く難しくなりがち • 解決3:漸進的型付け def add(x:Integer, y:Integer); x+y; end def add(x: String, y: String); x+y; end def add(x:α, y:α) where α.respond_to?(:+) x+y end
  34. 34. 型のその他の問題 • 型注釈を書くのが面倒 • 型注釈のない既存資産が大量に存在する – これらもだいたい漸進的型付けで解決できる
  35. 35. 目次 • 『Ruby でつくる Ruby』ダイジェスト – 概要 – インタプリタの構成と実装例 – ゴールと意義 • Ruby でつくる型付き Ruby – Ruby と型の概観 – 漸進的型付けのアイデアと実装例  今からここ • まとめ
  36. 36. 漸進的型付け [Siek ‘06] のアイデア • 型ありと型なしの同居を認める – 型注釈があったら型チェックされる – 型注釈がなかったら型チェックしない • Ruby での使い方 – ダックタイピングしたいところには型注釈を書かない – 型注釈のない既存資産もとりあえずそのまま使える (気が向いたときに書き足してもよい) def add(x, y) x + y end def add_int(x:Integer, y:Integer) x + y end
  37. 37. 漸進的型付けの実装イメージ • 型注釈がない変数は any 型とする • any 型が絡む演算の結果は any 型とする – すごく単純なアイデアだけど 2006 年頃にようやく登場 • 部分型(継承関係)を使った アプローチが研究の泥沼だった – 漸進的型付けは Python 3 の型ヒントや TypeScript の基盤でもある (つまり多分 Ruby でも使える?) J. G. Siek, et al. Gradual Typing for Functional Languages より引用
  38. 38. 実装してみた、デモ def add(x, y) x + y end def add_int(x:Integer, y:Integer) x + y end p(add(1, 2)) #=> 3 p(add("foo", 2)) #=> 実行時例外 p(add("foo", "bar") #=> "foobar" p(add_int(1, 2)) #=> 3 p(add_int("foo", 2)) #=> 実行前に型エラー p(add_int("foo", "bar") #=> 実行前に型エラー ※型注釈を含む構文解析は自作しました (本には未掲載、本で読みたい人はラムダノート社長の鹿野さんを煽ってください)
  39. 39. まとめ:型付き MinRuby • MinRuby を実験用環境として使う – クラスやブロックなど、難しいものがないので実験し やすい • 漸進的型付けの Ruby 実用化への課題 – クラスやブロックなど、難しいものにも対応しないと いけない • Python や TypeScript が行けたくらいだから多分できる? – 型推論の併用を考える • TypeScript は局所型推論みたいなことをやってる (関数の引数の方は推論しない)
  40. 40. 目次 • 『Ruby でつくる Ruby』ダイジェスト – 概要 – インタプリタの構成と実装例 – ゴールと意義 • Ruby でつくる型付き Ruby – Ruby と型の概観 – 漸進的型付けのアイデアと実装例 • まとめ  今からここ
  41. 41. まとめ • 『Ruby でつくる Ruby』 – Ruby 言語で Ruby インタプリタを作る本 • この会場で直販中! • インタプリタの原理と構成を 学べる • Ruby 言語を冷静に見つめ、 機能拡張とかを考えられる – 型とか

×