演算子オーバーライドをDSLに活用する

2,430 views

Published on

(Japanese) Fukuoka RubyKaigi01 Session

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
2,430
On SlideShare
0
From Embeds
0
Number of Embeds
23
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

演算子オーバーライドをDSLに活用する

  1. 1. 演算子オーバーライドを DSLに活用する makoto kuwata http://www.kuwata-lab.com/ FukuokaRubyKaigi01 1
  2. 2. copyright© 2008 kuwata-lab.com all rights reserved 基本アイデア ‣ 演算結果として構文木を返す ‣ "構文解析" ではなく "評価" x + y * 2 + x y 2 * 構文解析(parse) 評価(evaluate) 2
  3. 3. サンプルコード 3
  4. 4. copyright© 2008 kuwata-lab.com all rights reserved サンプルコード: クラス関係 class Node ... end class Expression < Node ... end class Variable < Node ... end 構文木要素 式 変数 4
  5. 5. copyright© 2008 kuwata-lab.com all rights reserved サンプルコード: Nodeクラス class Node def +(arg) return Expression.new(:'+', self, arg) end def *(arg) return Expression.new(:'*', self, arg) end .... end 演算結果として式を返す 5
  6. 6. copyright© 2008 kuwata-lab.com all rights reserved サンプルコード: Expressionクラス class Expression < Node def initialize(token, left, right) @token = token @left = left @right = right end attr_accessor :token, :left, :right end トークンと、 左右の Node を保持 6
  7. 7. copyright© 2008 kuwata-lab.com all rights reserved サンプルコード: Variableクラス class Variable < Node def initialize(name) @name = name end attr_accessor :name end 変数名を保持 7
  8. 8. copyright© 2008 kuwata-lab.com all rights reserved サンプルコード: 実行例 x = Variable.new('x') y = Variable.new('y') x + y * 2 #=> Expression.new(:'+', Variable.new('x'), Expression.new(:'*', Variable.new('y'), 1 ) ) + x y 2 * ...構文木をさらに変換 '+' より '*' の ほうが強い 8
  9. 9. 応用例 9
  10. 10. copyright© 2008 kuwata-lab.com all rights reserved 応用例:Ruby式をSQLへ # Ruby #=> SQL x == 10 #=> x = 10 x == nil #=> x is null x ** 3 #=> power(x, 3) Ruby式 構文木 SQL 参考: Operast (http://github.com/kwatch/operast/) 10
  11. 11. copyright© 2008 kuwata-lab.com all rights reserved 応用例:Ruby式をSQLへ # Ruby #=> SQL x > Date.new #=> x > '2008-12-06' x =~ '%pattern%' #=> x like '%pattern%' x.in? [1,4,7] #=> x in (1,4,7) x.in? 10..20 #=> x between 10 and 20 参考: Operast (http://github.com/kwatch/operast/) 11
  12. 12. copyright© 2008 kuwata-lab.com all rights reserved 応用例:O/R Mapper の Model class User set_table_name('users') @name = Variable.new('name') def self.name; return @name; end # or class <<self; attr_reader :name; end end User.name == nil #=> users.name is null 12
  13. 13. 問題点、その他 13
  14. 14. copyright© 2008 kuwata-lab.com all rights reserved 言語の必要条件 ‣ 演算子がオーバーライドできること • Ruby • Python • C++ (演算子オーバーロード) ‣ 型がないこと or 型が変更できること • 「==」の戻り値の型が boolean に固定されている と、実現不可能 14
  15. 15. copyright© 2008 kuwata-lab.com all rights reserved 問題点:オーバーライド不可 ‣ オーバーライドできない演算子がある • 例:Ruby1.8 では、「!=」は 「==の否定」に固定 ‣ 解決策: • 他の演算子で代用(「^」「<=>」など) • 「x.not」や「x.ne」(not equal) を提供 x.not == 1 #=> x != 1 15
  16. 16. copyright© 2008 kuwata-lab.com all rights reserved 問題点:&& と || ‣ 「&&」と「||」はオーバーライドできない • 評価のショートカットがあるため ‣ 解決策: • メソッドで代用(「.and」「.or」) • bit 演算子で代用(ただし演算子の優先順位に注意) (x == 1) & (y == 2) #=> x = 1 and y = 2 16
  17. 17. copyright© 2008 kuwata-lab.com all rights reserved 問題点:1 + x ‣ 「x + 1」は OK だが「1 + x」は NG ‣ 解決策:一時的に Integer#+ を override def sandbox begin Integer#+ に alias 名をつけて退避 Integer#+ をオーバーライド yield  ensure Integer#+ をもとに戻す end 17
  18. 18. copyright© 2008 kuwata-lab.com all rights reserved 問題点:動作速度 ‣ 動作速度は、たぶん遅い • 自作 O/R Mapper で評価中 ‣ 解決策: • なさそう? • キャッシュもできない(or 逆に遅くなる) • 最高速である必要はない (気にならない程度の遅さならよしとする) 18
  19. 19. まとめ 19
  20. 20. copyright© 2008 kuwata-lab.com all rights reserved まとめ ‣ 演算子をオーバーライドして構文木を返す •「構文解析」ではなく「評価」 •Ruby の式をそのまま使う → DSLで使いやすい ‣ アイデア次第で面白い使い方ができるかも 20
  21. 21. copyright© 2008 kuwata-lab.com all rights reserved 参考 ‣ SQLAlchemy (Python) • http://www.sqlalchemy.org/ ‣ Sequel (Ruby) • http://sequel.rubyforge.org/ ‣ RSpec (Ruby) • http://rspec.info/ ‣ Operast (Ruby) • http://github.com/kwatch/operast/ 21
  22. 22. one more thing... 22
  23. 23. copyright© 2008 kuwata-lab.com all rights reserved こんな面倒なこと、本当に必要? ‣ Lispなら、同じことが「'」だけでできる ‣ 言語として必要な機能を Ruby が提供して いないだけでは? (+ x (* y 2) ) ;; 式が計算される '(+ x (* y 2) ) ;; 式の構文木が返される Lisp最強 23
  24. 24. thank you 24

×