$30 off During Our Annual Pro Sale. View Details »

C++視点からのRuby紹介

 C++視点からのRuby紹介

第2回 mixC++勉強会@Tokyo での発表スライド

Ryohei Ikegami

March 21, 2014
Tweet

More Decks by Ryohei Ikegami

Other Decks in Programming

Transcript

  1. C++ 視点からの Ruby 紹介 @seanchas̲t

  2. 内容 C++ を使っている人向けに Ruby の概要を紹介 C++ と Ruby は思想が対照的 C++

    実行効率重視 Ruby 開発効率重視 C++ と Ruby は使い所がかぶらないことが多い Ruby で低レイヤ開発はしない C++ で Web アプリは作らない 比べると面白いかもしれない
  3. 自己紹介 Twitter: 情報工学科 2 年 結構長い間 Qt+C++ でお絵かきソフト作ってた 最近は Ruby

    をよく書いてる @seanchas̲t PaintField
  4. 目次 処理系 値とクラス メソッド クラスとモジュー ル ブロック 例外 メタプログラミング テスト

  5. 処理系

  6. C++ ネイティブコー ドへのコンパイラが基本 GCC Clang VC++ ... C++/CLI

  7. Ruby MRI (CRuby) 公式実装 バー ジョン 2.1.1 r u b

    y コマンド
  8. Ruby JRuby JVM で動く Java コー ドを呼べる IronRuby .NET で動く

    DLR (Dynamic Language Runtime)   ベー ス C# コー ドを呼べる dynamic で C# から IronRuby も呼べる
  9. REPL Run‑Eval‑Print‑Loop 一行ずつコー ドを読み込んで実行、 結果を表示 irb 標準 pry 色々 便利

    [ 6 ] p r y ( m a i n ) > 1 + 2 = > 3
  10. 値とクラス

  11. 動的型付け Ruby は動的型付き言語 変数に型は紐付いていない 値そのもの( オブジェクト) に 型( クラス) が紐付いている

    Ruby には変数宣言はなし x = 1 0 y = ' f o o ' y . c l a s s # = > S t r i n g y = x y . c l a s s # = > F i x n u m
  12. すべてが参照 値とポインタの区別はない すべて参照型 ( ポインタ ) どこからも参照されなくなったオブジェクトは GC される

  13. すべてがオブジェクト Ruby にはプリミティブ型は存在しない すべてのクラスは Object ( あるいは BasicObject) を継承 数値にもメソッドが使える

    - 1 . a b s # = > 1 1 0 . t i m e s d o p u t s ' h e l l o ' e n d
  14. 構文

  15. unless 文 if not を表す i f f o o

    p u t s ' f o o ' e n d u n l e s s f o o p u t s ' n o t f o o ' e n d
  16. 後置 if / unless 節が 1 文のみの場合、 if / unless

    を後置できる p u t s ' f o o ' i f f o o p u t s ' n o t f o o ' u n l e s s f o o
  17. 値を返す if / unless 節の最後の文の値を返す s t r = i

    f f o o p u t s ' r e t u r n i n g " f o o " ' ' f o o ' e l s e p u t s ' r e t u r n i n g " n o t f o o " ' ' n o t f o o ' e n d
  18. while / until until は while not を表す if と同様に後置が可能

    w h i l e c o n d p u t s ' l o o p ' e n d u n t i l c o n d p u t s ' l o o p ' e n d p u t s ' l o o p ' w h i l e c o n d
  19. break / next / redo いずれもルー プ内で使う break ルー プから抜ける

    C++ の break next 次の繰り返しに移る C++ の continue redo 同じ繰り返しを再度実行する C++ にはない
  20. break / next / redo w h i l e

    t r u e i n p u t = g e t s . t o _ i c a s e i n p u t w h e n 1 b r e a k w h e n 2 n e x t w h e n 3 r e d o e n d e n d
  21. case 値を指定して合致した節を実行 C++ の switch と似ているがより強力 値、 クラス、 正規表現などでマッチさせられる c

    a s e v a l u e w h e n 1 , 2 p u t s ' v a l u e = 1 o r 2 ' w h e n ' h o g e h o g e ' p u t s ' v a l u e = " h o g e h o g e " ' w h e n F l o a t p u t s ' v a l u e は浮動小数点数' w h e n / p a t t e r n / p u t s ' 正規表現/ p a t t e r n / にマッチ' e l s e p u t s ' それ以外' e n d
  22. String interpolation 文字列リテラルの中に式を記述して 評価結果を文字列化して埋め込む " 1 + 1 = #

    { 1 + 1 } " = > " 1 + 1 = 2 "
  23. 制御構造で 真になる値・ 偽になる値 nil, false 偽 nil は null の意味

    それ以外 真 0 も真
  24. | | / & & a | | b a

    ? a : b a & & b a ? b : a それぞれ同じ Ruby では | | / & & は真偽値ではなくオブジェクトを返 す
  25. nil ガー ド | | / & & 演算子を使って nil

    を綺麗に扱う a | | = b # a がなかったらb を代入 a & & = a . n a m e # a があればa をa . n a m e にする
  26. メソッド

  27. メソッド d e f a d d ( a ,

    b ) a + b e n d a d d ( 1 , 2 ) # = > 3 a d d 1 , 2 # = > 3 C++ の関数・ メンバ関数にあたるもの 最後の式が暗黙的に return される 明示的な return も書ける メソッド呼び出し時には括弧を省略できる メソッドをキー ワー ドのようにして扱える
  28. メソッド名 メソッド名の最後には? , ! , = が使える ? 真偽値を返すメソッド !

    , = これから説明
  29. bang method s t r = ' f o o

    ' s t r . u p c a s e # = > ' F O O ' s t r # = > ' f o o ' s t r . u p c a s e ! s t r # = > ' F O O ' 同じ名前のメソッドで 破壊的なものとそうでないものがあるとき、 破壊的な方に! を付ける習慣 破壊的 : 元のオブジェクトを変えてしまう C++ 的に言えば non‑const
  30. 代入メソッド c l a s s L a b e

    l d e f t e x t @ t e x t e n d d e f t e x t = ( t ) @ t e x t = t e n d e n d L a b e l . n e w . t e x t = ' f o o ' メソッドの名前の最後に= をつけると オブジェクトのフィー ルドへの代入を 表せるようになる メソッド呼び出しの括弧の省略とあわせると setter / getter を綺麗に書ける
  31. Splat d e f f o o ( * a

    r g s ) a r g s e n d f o o ( 1 , 2 , 3 ) # = > [ 1 , 2 , 3 ] f o o ( * [ 1 , 2 , 3 ] ) # 同じ * を仮引数名につけると 引数が配列にまとめられる * を実引数につけると 配列が展開されて引数になる
  32. クラスとモジュー ル

  33. クラス Ruby のクラスは単一継承のみ インスタンス変数 @ をつける インスタンス外からはアクセス出来ない s e l

    f C++ のt h i s 省略可能 c l a s s P u s h B u t t o n < B u t t o n d e f i n i t i a l i z e ( b u t t o n _ f i e l d , n a m e , p u s h e d ) @ n a m e = n a m e b u t t o n _ f i e l d . a d d ( s e l f ) p u s h i f p u s h e d # s e l f . p u s h i f p u s h e d と同じ e n d d e f p u s h @ p u s h e d = t r u e e n d e n d
  34. new と initialize b u t t o n =

    P u s h B u t t o n . n e w ( f i e l d , ' p r e s s m e ' , f a l s e ) インスタンスを作る際は、 n e w というクラスメソッドを呼ぶ i n i t i a l i z e は インスタンスが作られた時に呼ばれるメソッド n e w に渡された引数がそのまま渡される C++ でいうコンストラクタ
  35.   モジュー ル m o d u l e M

    d e f m o d u l e _ m e t h o d ' m e t h o d o f M ' e n d e n d c l a s s C i n c l u d e M e n d c = C . n e w c . m o d u l e _ m e t h o d # = > ' m e t h o d o f M ' クラスとよく似ているが、 インスタンスを作れない 他のクラスまたはモジュー ルから include して 機能の付加 (mixin) のために使う クラスは 1 つしか継承できないが モジュー ルは複数 include できる ( 多重継承 ) CRTP
  36. 特異メソッド s t r = ' h o g e

    ' d e f s t r . h o g e ? s e l f = = ' h o g e ' e n d s t r . h o g e ? # = > t r u e クラスやモジュー ルに対してだけではなく オブジェクト単体にもメソッドを定義できる 特異メソッド オブジェクトに対して特異クラスが作られ 特異メソッドはそこに定義される 動的型付き言語らしい機能
  37. クラスメソッド c l a s s F o o d

    e f s e l f . f o o ' f o o ' e n d e n d F o o . f o o # = > ' f o o ' C++ の static メンバ関数と同様な機能 Foo クラス ( s e l f ) の特異メソッドとして定義される 不思議な記法なのはそのため
  38. クラスメソッドと インスタンスメソッドの表記法 c l a s s F o o

    # F o o # f o o d e f f o o e n d # F o o . b a r d e f s e l f . b a r e n d e n d インスタンスメソッドは# クラスメソッドは. を付けて 表記されることが多い
  39. Class, Module クラス Ruby ではクラスもモジュー ルも普通のオブジェクト 実行時にも操作できる C++ の型はオブジェクトではなく テンプレー

    トメタプログラミングでないと 操作できないのと対照的
  40. クラス定義中のコー ド実行 c l a s s F o o

    p u t s ' d e f i n i n g F o o ' a t t r _ a c c e s s o r : h o g e # h o g e , h o g e = が定義される e n d クラス・ モジュー ル定義中には 普通にコー ドを実行できる Class/Module クラスの便利なメソッドも呼べる a t t r _ a c c e s s o r インスタンス変数の setter, getter を定義してくれる
  41. ブロック

  42. ブロック d e f f o o y i e

    l d ' f o o ' e n d f o o d o | s t r | p u t s s t r e n d # ' f o o ' と表示される f o o { | s t r | p u t s s t r } # ' b a r ' と表示される メソッドに対して引数とは別に渡される ラムダ式 ( 無名関数 ) にあたるもの do∼end, {∼} で囲む {∼} は一行の時によく使われる y i e l d で呼び出す
  43. ブロック d e f b a r ( & b

    l o c k ) b l o c k . c l a s s # = > P r o c b l o c k . c a l l ' b a r ' e n d 引数名に& を付けると引数として受け取れる P r o c クラスのオブジェクトになる
  44. Proc 手続き (procedure) を格納したオブジェクト C++ でいう関数オブジェクト ブロックからp r o c

    メソッドで作れる & を付けてメソッドに渡すとブロック代わりになる p r c = p r o c { | s t r | p u t s s t r } p r c . c l a s s # = > P r o c p r c . c a l l ( ' h o g e ' ) # ' h o g e ' f o o ( & p r c ) # ' f o o '
  45. ブロックを使うメソッド ブロックは構文のような見た目かつ汎用的なので Ruby の様々 な場所に用いられる

  46. # e a c h メソッド Array 、Hash などが持つ 要素1つずつに対して処理を行うメソッド

    Ruby では for 文の代わりの# e a c h が主に使われる C++ ならs t d : : f o r _ e a c h a r y = [ 1 , 2 , 3 ] a r y . e a c h d o | i | p u t s i e n d
  47. Enumerable コンテナを一般的に扱うモジュー ル Array 、Hash などが include している C++ の

    STL アルゴリズムのような機能を提供する # e a c h メソッドを元に機能が実装されている Ruby の# e a c h ≒ C++ のb e g i n ( ) , e n d ( ) ブロックを使った関数型プログラミング a r y = [ 1 , 2 , 3 ] a r y . e a c h d o | i | p u t s i e n d a r y . m a p { | i | i * 2 } # = > [ 2 , 4 , 6 ] a r y . s e l e c t { | i | i . e v e n ? } # = > [ 2 ]
  48. I n t e g e r # t i

    m e s 回数の決まった繰り返しを簡単に表せる 1 0 . t i m e s d o p u t s ' h o g e h o g e ' e n d
  49. 例外

  50. 例外 b e g i n f i l e

    = F i l e . o p e n ( ' s o m e _ f i l e ' ) # エラー があった時 r a i s e ' e r r o r ' r e s c u e p u t s ' e r r o r o c c u r e d ' e n s u r e f i l e . c l o s e e n d
  51. 例外 r a i s e キー ワー ドで例外を発生させる begin

    C++ の try rescue C++ の catch ensure 成功・ 失敗どちらにしても行う処理 Java, C# などの finally C++ だと代わりに RAII
  52. ブロックを使った RAII っぽい機能 F i l e . o p

    e n ( ' s o m e _ f i l e ' ) d o | f i l e | # 処理 e n d F i l e . o p e n メソッド ファイルを開いた後、 受け取ったブロックを実行 例外の発生有無にかかわらず 最後にファイルを閉じる スコー プを抜けると自動的にリソー スが閉じられる RAII と共通している
  53. メタプログラミング

  54. メタプログラミングとは? C++ 、Ruby どちらもメタプログラミングが有名 プログラムに関するプログラム コー ドを生成するプログラム クラス・ メソッドなどのメタなものに対する処理

  55. クラス・ メソッドの取得 クラスやメソッドは動的に取得することができる リフレクション s t r = ' s

    t r ' s t r . c l a s s # = > S t r i n g s t r . m e t h o d s # = > インスタンスメソッドのリスト ( 特異メソッド含む) S t r i n g . i n s t a n c e _ m e t h o d s # = > S t r i n g のインスタンスメソッドのリスト S t r i n g . m e t h o d s # = > S t r i n g のクラスメソッドのリスト
  56. クラス・ メソッドの取得 C++ で変数から型を取得 d e c l t y

    p e C++ にはメソッド一覧を取得する機能はない 最近、C++ 標準化委員会では リフレクションに関する議論がなされている そのうち入るかも?
  57. Method, UnboundMethod m = 1 . m e t h

    o d ( : e v e n ? ) m . c l a s s # = > M e t h o d m . c a l l # = > f a l s e u = I n t e g e r . i n s t a n c e _ m e t h o d ( : e v e n ? ) u . c l a s s # = > U n b o u n d M e t h o d u . b i n d ( 2 ) . c a l l # = > t r u e Method オブジェクト オブジェクトと関連付けられている Proc とよく似ている C++ だと、std::bind で束縛されたメンバ関数 UnboundMethod オブジェクト オブジェクトと関連付けられていない # b i n d で関連付けて Method に C++ だと、 メンバ関数ポインタ
  58. メソッドの動的定義 c l a s s F o o [

    : h o g e , : p i y o ] . e a c h d o | m e t h | d e f i n e _ m e t h o d m e t h d o m e t h e n d e n d e n d f = F o o . n e w f . h o g e # = > : h o g e d e f i n e _ m e t h o d メソッド名とブロックを与えて メソッドを動的に定義
  59. method̲missing c l a s s F o o d

    e f m e t h o d _ m i s s i n g ( n a m e , * a r g s , & b l o c k ) p u t s " m i s s i n g m e t h o d # { n a m e } " e n d e n d F o o . n e w . n o _ s u c h _ m e t h o d # m i s s i n g m e t h o d n o _ s u c h _ m e t h o d と表示される メソッドが見つからなかった時に呼ばれる デフォルトの挙動では NoMethodError を投げる
  60. method̲missing 例 : 標準ライブラリの OpenStruct r e q u i

    r e ' o s t r u c t ' s = O p e n S t r u c t . n e w s . m e t h o d s . g r e p ( / h o g e / ) # = > [ ] s . h o g e = ' h o g e ' s . m e t h o d s . g r e p ( / h o g e / ) # = > [ : h o g e , : h o g e = ] d e f i n e _ m e t h o d と組み合わせて 動的に新しいメソッドを定義可能 ORM (SQL などをラップするしくみ ) でも よく使われる ActiveRecord など
  61. eval 文字列を実行 ( 評価 ) n = 1 0 e

    v a l ( ' n * 2 ' ) # = > 2 0
  62. instance̲eval ブロックをそのオブジェクトの インスタンスメソッドと同じ文脈で実行 具体的に言うと、self がそのオブジェクトになる

  63. instance̲eval c l a s s C a l c

    u l a t o r d e f i n i t i a l i z e @ v a l u e = 0 e n d d e f a d d ( x ) @ v a l u e + = x e n d e n d C a l c u l a t o r . n e w . i n s t a n c e _ e v a l d o a d d 2 a d d 1 0 p u t s @ v a l u e # 1 2 e n d このような DSL 風のコー ドを書くこともできるが インスタンス変数にアクセスできてしまうので 欠陥…
  64. C++ と Ruby の メタプログラミングの対比 C++ のメタプログラミング プリプロセッサメタプログラミング テンプレー トメタプログラミング

    constexpr メタプログラミング Ruby のメタプログラミング 実行時メタプログラミング のみ
  65. テスト

  66. テスト Ruby ではテストがないとつらい 動的言語なので、typo などで実行時エラー になる テストを書けば typo も挙動も同時にチェックできる

  67. RSpec Ruby 用の有名なテストフレー ムワー ク 振る舞い駆動開発 BDD 最初に振る舞いを規定するテスト (spec) を書き

    それに基づいて実装を行うという考え方 d e s c r i b e P e r s o n d o d e s c r i b e ' # g r e e t ' d o i t ' r e t u r n s " H e l l o " ' d o e x p e c t ( P e r s o n . n e w . g r e e t ) . t o e q ( ' H e l l o ' ) e n d e n d e n d テストコー ドであるが Person#greet が' H e l l o ' を返すという 「 仕様」 を規定している
  68. RSpec メタプログラミングによる内部 DSL で、 自然言語に近い spec を記述できる

  69. まとめ

  70. まとめ C++ の視点から見た Ruby の特徴的な機能などを紹介した スクリプト言語らしいゆるい構文 動的性 メタプログラミング DSL など

    C++ 、 ゲー ムとはあまり関係なかったけど 何らかの参考になれば幸い