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.

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

2,735 views

Published on

(Japanese) Fukuoka RubyKaigi01 Session

Published in: Technology
  • Be the first to comment

  • Be the first to like this

演算子オーバーライドを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

×