Rubyã«ã¯ã³ã¼ãçã表ããªãã¸ã§ã¯ããè¤æ°ããã Method , UnboundMethod , Proc ã§ããã Continuation ã¯å°ãéããã©ãå®è¡ã³ã³ããã¹ããè¨æ¶ãã¦ãããªãã¸ã§ã¯ãã¨ããæå³ã§ã¯è¿ããã®ãããããã Ruby Way ãã«ã¯ããããããããããããã¨ã«ã¤ãã¦ãé©ãã»ã©ã®ãã¨ã§ã¯ããã¾ãããã¨æ¸ãã¦ããããã©ãç§ã¯é©ãããã§ãããããå¾®å¦ã«éãã®ã ãå°ã£ãããã ãããã便å©ãªã®ãããããªããã
ããã§ä»åã¯ãããã®æ¦è¦ãçºãã¦ã¿ããã¨æãã
æ®éã®ã¡ã½ãã
defã§ã¡ã½ãããå®ç¾©ããã®ãä¸çªæ®éã ããªã
class C def greeting(arg) puts "C#greeting reveived #{arg}" end def iterator yield 'iterator 1st' yield 'iterator 2nd' yield 'iterator 3rd' end local = 1 def ref_local puts local end end obj = C.new # ã¾ããæ®éã«å¼ã¹ã obj.greeting 1 # => C#greeting received 1 # å¼æ°ã®æ°ã¯ãã§ãã¯ãã¦ãããã obj.greeting 1, 2 # => ArgumentError: wrong number of arguments (2 for 1) # ãããã¯ä»ãå¼ã³åºãæ§æãRubyã®ç¹å¾´ããããã¯ãããããããã㯠obj.iterator do |item| puts item end # => iterator 1st # iterator 2nd # iterator 3rd # defã®å¤å´ã®ãã¼ã«ã«å¤æ°ã¯åç §ã§ããªããJavaScriptã¨ãã®æè¦ããããã¨ãããã ããã obj.ref_local # => NameError: undefined local variable or method `local' for #<C:0x5e1b8>
Methodãªãã¸ã§ã¯ã
Rubyã®ã¡ã½ããã¯ãªãã¸ã§ã¯ãåã§ãããç¹å®ã®ã¡ã½ããã«çµã³ã¤ããå¼ã³åºãå¯è½ãªã¡ã½ããã表ãã®ã Method ãªãã¸ã§ã¯ãã ãMethodãªãã¸ã§ã¯ããåå¾ããã«ã¯ãæ¢åã®ã¤ã³ã¹ã¿ã³ã¹ã®methodã¡ã½ããããã¡ã½ããåãã·ã³ãã«ãæååã§æ¸¡ãã¦å¼ã¶ã
greeting = obj.method(:greeting)
æ¬å¼§ãçç¥å¯è½ãªã¡ã½ããå¼ã³åºãæ§æã®ããã§ãJavaScriptãPythonã®ããã«obj.greetingã¨æ¸ãã¦ã¡ã½ãããªãã¸ã§ã¯ããåå¾ããã¨ãã訳ã«ã¯ãããªããMethodãªãã¸ã§ã¯ãã表ãèªç¶ãªè¡¨è¨ãåå¨ããªããããããRubyã§ã¯ã¡ã½ããã¯ãã¡ã¼ã¹ãã¯ã©ã¹ã®ãªãã¸ã§ã¯ãã§ãªãã¨è¨ãããçç±ã®1ã¤ã ã
greetingã¯æ®éã®ã¡ã½ããå¼ã³åºãæ§æã§ã¯å¼ã¹ãªããä»ãMethodãªãã¸ã§ã¯ããåç §ãã¦ããgreetingã¯ãã¼ã«ã«å¤æ°ã ããã©ããã¡ã½ããå¼ã³åºãã¯ã¡ã½ããç¨ã®åå空éãæ¢ç´¢ããããNoMethodErrorã«ãªããRubyã®å ´åãå¤æ°ã¨ã¡ã½ããã¯åå空éãå ±æãã¦ãªãã®ã ã
greeting(1) # => NoMethodError: undefined method `greeting' for main:Object
ã¡ãªã¿ã«greetingã¨ã ãæ¸ãã¦ãå¼æ°ã®æ°ã§ã¨ã©ã¼ã«ãªã£ããã¯ããªããããã«ãããããã«ãããã¯ãã¼ã«ã«å¤æ°greetingã ããå«ãå¼ã¨ãã¦è©ä¾¡ãããã
greeting # => #<Method: C#greeting>
Methodãªãã¸ã§ã¯ããå¼ã³åºãã«ã¯callãã使ããããªã¼ãã¼ã©ã¤ãããã¦ãã®ã¯ãé常ã®æ¬å¼§ä»ãå¼ã³åºãã«å°ãã§ãè¿ã表è¨ã§ãã¨ãããã¨ã
greeting.call 1 # => C#greeting received 1 greeting[1] # => C#greeting received 1
å¼æ°ã®æ°ãã¡ããã¨ãã§ãã¯ãã¦ãããã
greeting[1, 2] # => ArgumentError: wrong number of arguments (2 for 1)
ãããã¯ã渡ãã
iterator = obj.method(:iterator) iterator.call do |item| puts item end # => iterator 1st # iterator 2nd # iterator 3rd
å¼ã³åºãæ§æ
ã©ãã«ããæ®éã®ã¡ã½ããã®ããã«ã¯å¼ã¹ãªãã®ã¯æ©ã¾ããç¹ã§ãã¾ã¤ãã¨ãããæ©ãã§ãæ§åãã callãçç¥ãã ãã¨ããRuby 1.9ã§ã¯è¡ã£ããæ¥ãããã¦ãã
parse.y version 1.372
parse.y version 1.372 ã®å¤æ´ããã¼ã«ã«å¤æ°ã«æ¬å¼§ãã¤ããã¨ãæé»ã«callã«å¤æããããããã«ãªã£ãã
greeting("v 1.372") # => C#greeting received v 1.372 greeting "v 1.372" # => C#greeting received v 1.372
æ¬å¼§ã®çç¥ãã§ãããããããã°ãããã
parse.y version 1.382
parse.y version 1.382 ãæé»ã®å¤æã¯ããããã¼ã«ã«å¤æ°ã®ã»ãã«ãæ¬å¼§ãã¤ãããã¨ãã®ã¿ãæ¬å¼§ã§å¼ã³åºãããã¨ã«ãªã£ãã
(greeting)("v 1.382") # => C#greeting received v 1.382
ãã®æ§æã¯Cè¨èªã®é¢æ°ãã¤ã³ã¿ãå ãã¿ãªæ°ããããRubyå¦çç³»ã®ã³ã¼ãã¯K&Rã¹ã¿ã¤ã«ã ããã¡ãªã¿ã«ãã©ã¡ãã®æ¬å¼§ãçç¥ã¯ã§ããªãã
greeting(1) # => undefined method `greeting' for main:Object (NoMethodError) (greeting) 1 # => parse error
ãã£ã±ããã
parse.y version 1.442 ã®å¤æ´ã§ããã®å¼ã³åºãæ§æã¯ç¡ããªã£ãã
(greeting)(1) # => parse error, expecting `$'
_sendã¨ã®æ¯è¼
Methodãªãã¸ã§ã¯ãã®å¼ã³åºãã¯ã©ãã«ãRubyã«ãã¦ã¯ãã¾ãã¡æ§æãæ ¼å¥½æªãã®ã ããã©ããã§ãå®ã¯ãã¾ãå°ããªãããªããã¨ããã¨ããããMethodãªãã¸ã§ã¯ãããã¾ã使ããªããããå¦çå ã®ã¡ã½ãããåãæ¿ããã ããªãããªãã¸ã§ã¯ãã«éä¿¡ããã¡ãã»ã¼ã¸ãä¿åãã¦ããã¦sendããæ¹ã楽ãªã®ã ã
msg1 = [:greeting, 1] msg2 = [:greeting, "Hello"] msg3 = [:inspect] obj.__send__(*msg1) # => C#greeting reveived 1 obj.__send__(*msg2) # => C#greeting reveived Hello obj.__send__(*msg3) # => "#<C:0x2aa33c>"
Methodãªãã¸ã§ã¯ãã®å©ç¹ã¯ããããã¡ã½ããæ¬ä½ã®ã³ã¼ãåã¸ã®åç §ã§ããã¨ãããã¨ã ãã ãããMethodãªãã¸ã§ã¯ããä¿åãã¦ããã°ãã¡ã½ãããæ¸ãæãã¦ãã¾ã£ãå¾ã§ããåé¤ããå¾ã§ãå ã®ã¡ã½ãããå©ç¨ã§ããã
greeting = obj.method(:greeting) class C def greeting(arg) puts "Yet another ruby hacker" end end obj.greeting("test") # => Yet another ruby hacker greeting.call("test") # => C#greeting received test class C remove_method :greeting end obj.greeting("test") # => NoMethodError: undefined method `greeting' for #<C:0x2aa33c> greeting.call("test") # => C#greeting received test
sendã®å ´åããªãã¸ã§ã¯ãã«éãããã¡ãã»ã¼ã¸ãä¿åãã¦ããã¦ããã®é½åº¦ã¡ã½ãããã£ã¹ããããã¦ããã ããªã®ã§ãã¡ã½ããã®å®ç¾©ãæ¸ãæãã¦ãã¾ãã¨æ¸ãæãå¾ã®å¼ã³åºãã¯å½±é¿ããããã¨ã«ãªããMethodãªãã¸ã§ã¯ãã®å ´åã¯ãæ¸ãæããåã®ã¡ã½ããã®å®ç¾©ãã®ãã®ããã¤ã³ããã¦ãããªãã¸ã§ã¯ããªã®ã§ãå¼ã³åºãã¨ãã«ã¯ã¡ã½ãããã£ã¹ãããã¯çºçããªããããã¯Object#methodãå¼ãã ã¨ãã«çºçãã¦ãã¾ã£ãã®ã ãã ãããå¾ããã¡ã½ãããæ¸ãæãã¦ããæ¸ãæãåã®å®ç¾©ã§åä½ããã
UnboundMethod
Methodã¯ãã¡ã½ããå®ç¾©ã¨ã¬ã·ã¼ãã¼ãçµã«ãããããªãªãã¸ã§ã¯ãã ãããããã¬ã·ã¼ãã¼ãæããã®ãUnboundMethodã ãC#ã®delegateã«è¿ãã®ãMethodãªãã¸ã§ã¯ãã ã¨ããã°ãC++ã®ã¡ã³ãé¢æ°ãã¤ã³ã¿ã«ä¸çªè¿ãã®ãUnboundMethodã ãC++ã ã¨Methodç¸å½ã®ãã®ã¯ã¡ã³ãé¢æ°ãã¤ã³ã¿ã使ã£ã¦boostãããã®Functorã¿ããã«å®è£ ããããã
UnboundMethodã¯Module#instance_methodãMethod#unbindã§ä½æããã
u_greeting = greeting.unbind u_iterator = C.instance_method(:iterator)
Rubyããã°ã©ã ã§ã¯ã©ãã«ã§ãselfãããã®ã§ãã£ããä¸è¦é¢æ°ã«è¦ãããã®ãRubyã§ã¯å ¨é¨ãªãã¸ã§ã¯ãã¸ã®ã¡ãã»ã¼ã¸éä¿¡ã ãããã¬ã·ã¼ãã¼ã®ãªãUnboundMethodã¯å®è¡ã§ããªãã
UnboundMethodã«ã¯Methodã¨åãcallããå®ç¾©ããã¦ããããã©ããããã¯å¤åãRuby 1.6ã®é ãUnboundMethodãMethodã®ãµãã¯ã©ã¹ã ã£ãã¨ãã®åæ®ãã©ã£ã¡ã¿ã¡ãcallããå¼ãã§ãTypeErrorãçºçããã
ã§ãä»æ¹ããªããããå¼ã³åºãã¨ãã¯UnboundMethod#bindã§ã¬ã·ã¼ãã¼ãè¨å®ãã¦ãã使ããUnboundMethod#bindã¯Methodãªãã¸ã§ã¯ããè¿ãã®ã§ã使ãæ¹ã¯ãã£ãã¨åãã
iterator2 = u_iterator.bind(C.new) iterator2.call do |item| puts item end
UnboundMethodããå ã®ã¡ã½ãããæå±ãã¦ããã®ã¨ã¯éãã¯ã©ã¹ã®ã¤ã³ã¹ã¿ã³ã¹ã«bindãããã¨ã¯ã§ããªããUnboundMethodçµç±ã§ã¯ã©ã¹ããã¯ã©ã¹ã¸ã¡ã½ãããã³ãã¼ã§ããã便å©ãªã®ã«ãå®éã«ã¯
u_iterator.bind(OtherClass.new)
ã¨ãããã¨ãTypeErrorã«ãªããã¾ããCè¨èªã§å®è£ ããã¦ããã¡ã½ãããªããã¯åã«ã·ãã¢ã ã£ãããããããå¤ã«ã³ãã¼ãããã¨å±ãªãã®ãããããªãã
Methodã¨UnboundMethodã®ä½¿ãåãã¯ã¾ããèªæã¨ããããMethodã¯selfãã»ããã§æã¡æ©ãããæã«ä½¿ããUnboundMethodã¯selfã¯ãããªãã¨ãããããselfã¯ä½¿ãã¨ãã«åãã¦æ±ºã¾ããããªå ´åã«ä½¿ããMethodã¯ã¤ã³ã¹ã¿ã³ã¹ãå ç«ã£ã¦ããªãã¨ä½ããªããã©ãUnboundMethodã¯ã¯ã©ã¹ãªãã¸ã§ã¯ãããä½ãããã¨ãã
ã¡ãªã¿ã«ãRubyå¦çç³»å é¨ã®å®è£ ã§ã¯ãMethodãUnboundMethodãã©ã¡ããstruct METHODã ãUnboundMethodã¯selfãè¨é²ãã¦ãããã¤ã³ã¿ã使ã£ã¦ãªãã ãã
æ®éã®Proc
ãã¦ãã¦ã次ã¯ProcããProcãªãã¸ã§ã¯ãã¯ã³ã¼ãçã表ããªãã¸ã§ã¯ãã§ããããã¯ãå ã«ãã¦ä½æããã®ãæ®éã bãProc.newã¾ãã¯å義èªã§ããKernel#lambdaã§ä½æããããããã¯ããããã¯ä»ãã§å¼ã³åºãã¨ãã®ãããã¯ã®é¨åãProcãªãã¸ã§ã¯ãã«ã¾ã¨ãã¦ãããã¡ã½ããã ã
å é¨çã«ã¯Procãªãã¸ã§ã¯ãã¨ã¡ã½ããå¼ã³åºãã«ãã£ã¤ãã¦ããããã¯ã¯å°ãéããã®ã§ãRubyã®å¤ããã¼ã¸ã§ã³ã§ã¯å¤§åã¸ã£ã³ãã¾ããã§æåãå°ãéã£ãããã¦ãããbreakããã¨ãã¨ããretryããã¨ãã¨ãã Procã®ããã¥ã¡ã³ã ã«è©³ããæ¸ãã¦ããã
Procã¯MethodãUnboundMethodã¨ã¯æ¯è²ãéããå ã«ãªãã¡ã½ãããåå¨ããªãã¨ããç¹å®ã®ãªãã¸ã§ã¯ãã«å±ããæå³åããèã(åããã¨ã)ã£ã¦ããã®ã大ãããã©ããã以ä¸ã«åºæ¬çã«ã¯cloureã§ããç¹ãéãã
def create_closure counter = 0 Proc.new { p counter += 1 } end c = create_closure c.call # => 1 c.call # => 2 c.call # => 3 ....
create_closureã¡ã½ãããæããæç¹ã§æ¢ã«ãã¼ã«ã«å¤æ°counterã®å±ããã¹ã³ã¼ãã¯åå¨ããªããããã©ããProcãªãã¸ã§ã¯ãã¯å é¨ã«é¢é£ãããã¼ã«ã«å¤æ°ãè¨æ¶ãã¦ãã¦ããããã£ã¦Procãªãã¸ã§ã¯ãã®ä¸ããã¯ããã®ãªãã¸ã§ã¯ããæ¶æ» ããã¾ã§ãã¼ã«ã«å¤æ°ã«ã¢ã¯ã»ã¹ã§ããã
Procãªãã¸ã§ã¯ããå é¨ã«æã£ã¦ãã®ã¯ããã®Procãä½ãããã¨ãã®ãã¼ã«ã«å¤æ°ç°å¢ã§ãã£ã¦ã次å以éã®craete_closureå¼ã³åºãã§æ°è¦ã«ä½ãããç°å¢ã¨ã¯é¢ä¿ãªãã
c1 = create_closure c1.call # => 1 c1.call # => 2 c1.call # => 3 c2 = create_closre c2.call # => 1 c2.call # => 2 c1.call # => 4 c1.call # => 5 c1.call # => 6 c1.call # => 7 c2.call # => 3 c1.call # => 8 c2.call # => 4
c1ãåç §ãã¦ããcounterã¨c2ãåç §ãã¦ããcounterã¯å¥ç©ã ãã¾ããã¤ã³ã¹ã¿ã³ã¹å¤æ°ã¿ãããªããã ã
ã§ãä½ãããããã®ãã¨ããã¨ããã¿ã« Curryå ã¨ã?ãããããã°ãProcãªãã¸ã§ã¯ããã®ãã®ãæä½ããããã°ã©ã ã£ã¦ãã¾ãæ¸ããªããã
ããããã®ã¯ããã2ã¤ã®ãªãã¸ã§ã¯ãã«å¤æ°ãå ±æãããããã©ãã°ãã¼ãã«å¤æ°ãã¯ã©ã¹å¤æ°ã«ã¯ããããªãã¨ãã
def create_twin shared = 0 return [ Proc.new { p shared += 1 }, Proc.new { p shared += 1 } ] end dee, dum = create_twin hikaru, kaoru = create_twin dee.call # => 1 dum.call # => 2; deeã¨dumã¯sharedãå ±æ hikaru.call # => 1 kaoru.call # => 2; ãã£ã¡ã¯ã¾ãå¥ã®ãã®ãå ±æ
ã¾ããRubyããã¯Perlã§ããè¦ãææ³ã ããã©ããã¨ãJavaScriptã§ãã©ã¤ãã¼ããªã¡ã½ãããå®ç¾©ãã1ã¤ã®æ¹æ³ã¯ã¯ãã®ææ³ã®å¿ç¨ã ãããAjaxã¾ãããããã¨Closureã®ããããã¿ã¯èªç¶ã«ä½é¨ã§ããã¯ãã
(2006-11-17: ma2 ããã®ææã«ããããã°ã©ã ä¾ã®ãã°ä¿®æ£)
ProcãMethodã¨åããå¼æ°ãåãããå®å¼æ°ã¯ããããã¯å¼æ°ã«æ¸¡ãã®ã§æ¬¡ã®ããã«æ¸ã
sum = 0 acc = Proc.new{|num| p sum += num} acc.call(3) # => 3 acc.call(2) # => 5
ãããã¯æ§æ
ã§ãProcã¯Proc.newã«ãã£ã¦çæããããã¯ããããã¯ä»ãã¡ã½ããã®å®è£ ã«ä½¿ãå ´åãå¤ãæ°ãããã
def iterator(a, b, &block) ... end
ã¨ãæ¸ãã¦ããã¦ãã®ã¡ã½ããããããã¯ä»ãã§å¼ã¶ã¨ãä»®å¼æ°blockã«æ¸¡ãããããã¯ãProcåãããã®ãå ¥ããä¾ãã°ãããã¯ç¹å®ã®ãªãã¸ã§ã¯ãã®ç¹ç°ã¯ã©ã¹ã®ã³ã³ããã¹ãã§ãããã¯ãè©ä¾¡ããã¡ã½ããã
def singleton_class_eval(&block) (class << self; self end).class_eval(&block) end
ããã§ã¯åãåã£ããããã¯ããã®ã¾ã¾class_evalã«æ¸¡ãã¦ãã¾ã£ã¦ãããããã¾ããªãã¸ã§ã¯ãåãã¦ãããããã¿ã¯ãªããããããªããã§ãä¸åº¦ãªãã¸ã§ã¯ãã¨ãã¦åãåã£ã¦ãã¾ãã°ãããã§ãå å·¥ã§ããããã ããã夢ãã²ãããã
DSL
ãããRailsã®DSLã«ãªãã¨ãåãåã£ãProcãªãã¸ã§ã¯ããã¤ã³ã¹ã¿ã³ã¹å¤æ°ã«ä¿åãã¦ããã¦å¿ è¦ãªã¨ãã«èµ·åãããããªã®ãå¤ç¨ãã¦ããRubyKaigi2006ã§ããã¹ããã¦ããã£ãã¨ãã«è©±ããããã©ãRailsã®å®£è¨çãªè¡¨è¨ãå¯è½ãªã®ã¯ã宣è¨æãå®è¡ããã¨ãã¨ãããã¯é¨åãå®è¡ããã¨ããå¥ã ã ãããä¾ãã°ãvalidationã«ä»ãã:ifãªãã·ã§ã³ã
class UserRegistration < ActiveRecord::Base validates_presence_of :phone, :if => Proc.new{|reg| reg.stage >= 1} .... end
ä¾ãã°ãã¦ã¼ã¶ã¼ç»é²ãè¤æ°ã®ç»é¢ã«åããã¦ã¦ãæåã®ç»é¢ã§ã¯validates_presence_of :phoneãæå¹ã«ããããªãã¨ããããç»é¢ã®çªå·ãstageå±æ§ã«å ¥ãã¦ããã¨ãã¦ããããªé¢¨ã«æ¸ãããActiveRecordã¯å¼æ°ã¨ãã¦åãåã£ãProcãªãã¸ã§ã¯ããå é¨ã«ä¿åãã¦ããã¦ãUserRegistrationã®validationãèµ°ããã³ã«UserRegistrationã®ã¤ã³ã¹ã¿ã³ã¹ãå¼æ°ã¨ãã¦Procãªãã¸ã§ã¯ããå®è¡ãããProc.newãå®è¡ããã®ã¯ã¯ã©ã¹ã®å®ç¾©æã ãã©ãProc.newã«æ¸¡ãããããã¯ã®ä¸èº«ã¯ãã£ã¨å¾ã§å®è¡ãããã
å¼ã³åºã表è¨
Procãªãã¸ã§ã¯ãã®å é¨ã®ã³ã¼ããå¼ã³åºãã«ã¯ãMethodãUnboundMethodã¨åæ§ã®ã¨callããããããã¦ããã®ä»ã«Proc#yieldãããã
ã¨callã¯MethodãUnboundMethodã®å ´åã¨åããå¼æ°ã®æ°ããã§ãã¯ãã¦ããããããã©ããyieldã¯å¼æ°ã®æ°ããã§ãã¯ããªããããã¯ãããã¯ä»ãã¡ã½ããã®ä¸ã§yieldæãå®è¡ããå ´åã¨åãã ãä¾ãã°ã
100.times do print "Hello" end
Kernel#timesã¯ãããã¯ã«ã«ã¦ã³ã¿ã®å¤ã渡ãããã©ãããããªé¢¨ã«ã«ã¦ã³ã¿ã®å¤ã¯ä½¿ããªãå ´åãå¤ããå¤åããã§ããããã¯ãyieldããã¨ãã«ã¯å¼æ°ã®æ°ã®ãã§ãã¯ãå ¥ããªãã®ã ã¨æããæ¯åº¦æ¯åº¦å¿ ã 100.times do |i| ã¨æ¸ããã°ãªããªãã®ã¯å«ã ãããã¦ãProc#yieldã®ã»ãã¯ããã«ããããä»æ§ã«ãªã£ã¦ããã
Procã®ä½¿ãã©ãã
å ã«ãæããããã©ãProcã使ãä¸çªãããã¡ãªã±ã¼ã¹ã¯ãããã¯ãåãä»ããã¡ã½ãããå®è£ ããã¨ãã
ãããããä¸ã®create_twinã®ä¾ã®ããã«ãclosureã¨ãã¦ã®Procã¯å®å¿ããããããä½æ ãã¨ããã¨ãProcãåç §ãã¦ããå¤å´ã®ãã¼ã«ã«å¤æ°ã¯ãProcãçæããã³ã³ããã¹ããæãã¦ãã¾ã£ããæå¾ãProc以å¤ããã¯åç §ã§ããªããããæ¸ãæããå¿é ããªããcreate_twinãæãã¦ãã¾ã£ããããã®å¾ã¯çæãããProcã®ãã¢ä»¥å¤ããã¯counterãåç §ãããã¨ã¯ã§ããªããå³å¯ã«ã¯æãéã¯ããããã©ããã®å ´åã¯ããã¨åããã³ã¼ãã«ãªããã大ä¸å¤«ã
ãããã¤ã³ã¹ã¿ã³ã¹å¤æ°ã§ä»£ç¨ãããã¨ããã¨å°ã£ããã¨ã«ãªããã¤ã³ã¹ã¿ã³ã¹å¤æ°ã¯ãã®ã¤ã³ã¹ã¿ã³ã¹å ã§ã¯èªç±ã«ã¢ã¯ã»ã¹ã§ãã¦ãã¾ããããç¹ã«ãµãã¯ã©ã¹ã¨ã®ã¤ã³ã¹ã¿ã³ã¹å¤æ°åã®è¡çªã¯å°ã£ãåé¡ã§ãRuby 2.0ã§ã¯æ¹åãããå¯è½æ§ãããããã©ç¾ç¶ã¯ã©ããããããªãã
誰ãã©ããªæ¡å¼µãããã®ãåãããªãã©ã¤ãã©ãªãä½ãå ´åãã¤ã³ã¹ã¿ã³ã¹å¤æ°ããã¯æ¢ãã¦closureã®å¤é¨ã¹ã³ã¼ãåç §ã使ãå ´åãããã
Continuation
Continuationã®æ¦å¿µã¯åãã£ã¦ãã¾ãã°é常ã«èªç¶ãªã®ã ãã©ã説æããã®ã¯ã¨ã¦ãé£ãããå¤åãContinuationãåãã¦ä½¿ãè¦ããã«ã¯RubyããSchemeã®ã»ãããããç§ãã ããã°ã©ãã³ã°è¨èªSCHEME ããèªãã¾ã§Rubyã®Continuationã使ããªãã£ããã
Rubyã®ç¹å¾´ã®ä¸ã¤ã¯æ¨æºã§Continuationããµãã¼ããã¦ãããã¨ã ã¨ã¯æããParrotã確ãRuby移æ¤ãè¦æ®ãã¦Continuationãå®è£ ããã¨ãè¨ã£ã¦ãããã§ããå®ã¯æå¹ã«æ´»ç¨ããã¦ããäºä¾ã¯ãããªã«å¤ããªããã¾ã¤ãã¨ããããå®è£ ã§ããããä½ã£ã¦ãã¾ã£ããã¨ãã£ã¦ããããã§ç©æ¥µçã«ãµãã¼ãããã¦ãæãã§ã¯ãªããããã ãããYARVã¸ã®Continuationå°å ¥ã«ãã¾ãä¹ãæ°ã§ãªãã®ã§Ruby 2.0ã§æ¶ããå¯è½æ§ããªãã§ã¯ãªãããããã¦æ¬²ãããªãã®ã§ç§ã¯ããã ããã®é¡è¦ãã¨Continuationããªã¼ãºã¨è¨ã£ã¦ãããã©ãã
Continuationã¯ããããããå ãããã°ã©ã çµäºã¾ã§ã®å¦çãã表ããªãã¸ã§ã¯ãã§ãããä½æã«ã¯Kernel#callccã使ãã
cont = nil callcc {|c| cont = c } puts :ok exit
ä¾ãã°ããããªã³ã¼ããèãã¦ã¿ããcallccã¯
puts :ok exit
ã¨ããããããããå ã®å¦çãã表ãContinuationãªãã¸ã§ã¯ãããããã¯å¼æ°ã¨ãã¦ãããã¯ãè©ä¾¡ãããããã§ã¯ããããã¯å ã§ãªãã¸ã§ã¯ããä¿åãã¦ãããããã§ä½ãå¬ãããã¨ããã¨ãä¿åãã¦ãããContinuationãªãã¸ã§ã¯ããcallããã°ããã¤ã§ãcallccã®æ¬¡ããå¦çãåéã§ããã®ã§ããããããããã¼ã«ã«å¤æ°ã®ã³ã³ããã¹ãã¯ãã®ã¾ã¾ä¿åããã¦ãã
ä¸ã§ã¯ãããããå ã®å¦çããããããããªããªãããã«æ£æçã«exitãç½®ãã¦ããã°ã©ã ãçµäºããã¦ãããããã©ããããç°¡åã§ãªãå ´åã®æ¹ãå¤ãããã®ã¨ãã«ããContinuationãæ¬é ãçºæ®ããã®ã ã
ä¾ãã°ããããªã®ã¯ã©ãã ãããRubyKaigiã®æã®ãã¬ã¼ã³ã«æ¸ããã³ã¼ãã®ãã°ãã£ã¯ã¹çã ãã©ã
@cont = [] ActiveRecord::Base.transaction do catch :save_tx do collection.each do |item| .... callcc{|c| @cont.push c; throw :save_tx} if something? .... end end end unless @cont.empty? ActiveRecord::Base.transaction do @cont.pop.call end end
ActiveRecord::Base.transactionã¯ããã¼ã¿ãã¼ã¹ãã©ã³ã¶ã¯ã·ã§ã³ãéå§ãã¦ãããã¯ãè©ä¾¡ãããããã¯ãæ£å¸¸çµäºããã¨ãã©ã³ã¶ã¯ã·ã§ã³ãã³ããããä¾å¤çºçæã«ã¯ãã¼ã«ããã¯ããã¡ã½ããã ãã§ããä½ãã®æ¡ä»¶ãæç«ããã¨ãã«ã¯æ©ãã«ã³ããããã¦ãã¾ã£ã¦ãæ´ã«ããããå¼ãç¶ãå¦çãããå ´åããªãã ããããä¸ã¯ãä¸åã³ããããã¦ãã¾ããæ©è½ãä»ãå ãããã®ã ã
- 2è¡ç®ãã¾ããæ®éã«ActiveRecord::Base.transactionã«å ¥ãã
- 3è¡ç®ã catch ã使ã£ã¦ãã¹ãããã®è±åºãæºåãããããã¯Javaããªããã®ã©ãã«ä»ãbreakã¨åãã§ããã¹ããããããã¯ãå¶å¾¡æ§é ããä¸æ°ã«è±åºãããã®ã ã次ã¨åãã ã¨èãã¦è¯ãã
save_tx: for (Object item : collection) { .... }
- 4è¡ç®ããããã¦ãã§ã¯ãããcollectionã®åè¦ç´ ã«ã¤ãã¦å¦çããã¦ããã¼ã¿ãã¼ã¹æä½ãããã
- ãããåè¦ç´ ã«ã¤ãã¦ç¹°ãè¿ã
- 6è¡ç®ãããã¦ãããæ¡ä»¶ãæç«ããã¨ããcallccãå®è¡ããã
callccã¯ããã®æ¬¡ã®è¡ããã®ãããcallccãæ¸ããã¦ããªããã°è¡ããã¦ããã§ãããå¦çãContinuationãªãã¸ã§ã¯ãã«å¤æãããããã¯ãè©ä¾¡ãã
- ãããã¯ã¯ããã@contã®ä¸ã«ä¿åããKernel#throwã§å ã»ã©ã®Kernel#catchã§ä½ã£ã:save_txã©ãã«ã«åãã¦è±åºãã
9è¡ç®ãããã¨ãcatchã®ãããã¯ãæããã¨ãã®å¾ã«ã¯ä½ããªãããããã®ã¾ã¾ActiveRecord::Base.transactionã®ãããã¯ãæãã¦ãã¾ããæ£å¸¸ã«æãããã¨ã«ãªãã®ã§ãActiveRecord::Base.transactionã¯ãã©ã³ã¶ã¯ã·ã§ã³ãã³ãããããã
11è¡ç®ããã¦ã@cont.empty?ã§ããã¨ã¯ãcallccã§ã¯ãªããæ®éã«collectionã®eachãçµãã£ã¦æãã¦ãããã¨ãæå³ãããããã»ã©callccã®ä¸ã§@contã«continuationãä»£å ¥ãããããä»ã¯empty?ã§ãªãã
- 12è¡ç®ãããã¨ãActiveRecord::Base.transactionãå©ç¨ãã¦æ¬¡ã®ãã©ã³ã¶ã¯ã·ã§ã³ãéå§ããã
13è¡ç®ã@contã®è¦ç´ ãåãåºãã¦ããããcallããã
- popãããcontinuationã¯ãcallccã®æ¬¡ããã®å¦çããè¦ãã¦ãããããå¿ è¦ãªããã¼ã«ã«å¤æ°ã復å ãã¦ãcallccã®æ¬¡ã®å¦ç(7è¡ç®)ã¸å¶å¾¡ã移ã
- ããã¦ãæ°ãã¤ãã¨ã¾ãcollection.eachã®ä¸ã«ããã®ã§ç¹°ãè¿ããå§ãã(8è¡ç®)
- ã¾ããsomething?ãæºããããã°åããã¨ãç¹°ãè¿ã(6è¡ç®)
- ãã¤ãã¯collection.eachã®ã»ããçµäºãã(8è¡ç®)
- 10è¡ç®ããã®ã¨ãã¯ããã¯ãActiveRecord::Base.transactionã¯ãããã¯ãæ£å¸¸çµäºããã¨è¨ããã¨ã§ã³ãããããã
- 11è¡ç®ãä»åº¦ã¯@contã¯ç©ºãªã®ã§å¦çãçµãã
ãããããã ãããããããããã¨æãããããªã®åãã£ã¦ãããã¨ãã人以å¤ã¯ç·´ç¿ãå ¼ãã¦ãã¾ã¤ãã¨ãããæ¸ãã CGIããã°ã©ã ããã®ã¾ã¾FastCGIåããã©ã¤ãã©ãª ãèªãã¨è¯ãã¨æãã
å¼ã³åºããã
Continuationãªãã¸ã§ã¯ãã¯ãã¯ãã¨callã«ãã£ã¦å¼ã³åºãããå¼ã³åºããå¯è½ã§ãã³ã³ããã¹ããè¨æ¶ãã¦ããã£ã¦ããæå³ã§Continuationã¯ãã¯ãMethod/Procã®ä»²éã§ã¯ããããã©ããä¸èº«ã¯ããªãéãããããã«Continuation#callã¯ååçã«å¶å¾¡ãè¿ããªãã
ã¡ãªã¿ã«ãcallãã«æ¸¡ããå¼æ°ã¯Arrayã«æããå½¢ã§ãæ»ã£ã¦ããã¨ãã®callccã®è©ä¾¡å¤ã«ãªããããã使ã£ã¦æåã«callccãå®è¡ããã¨ãã¨Continuation#callã§æ»ã£ã¦ããã¨ããæ¯ãåãããã¨ãã§ããã ããããæ»ã£ã¦ããç°å¢ã«æ å ±ã渡ããã¨ãã§ãããCãç¥ã£ã¦ã人ã¯setjmpã®æ»ãå¤ã ã¨æãã°è¯ãã¨æãã
Continuationã®ä½¿ãã©ãã
Continuationã¯æ £ããã¨ã¨ã¦ãèªç¶ãªæ¦å¿µã ãæ¬å½ã«ããããããå ã®æ®ãããç¶ããã ãããç¶ããä¿åãã¦ããã¦å¶å¾¡ãä¸æçã«ä½æã¸ããããã¨ãã«ä¾¿å©ã§ãããThreadã§ãã§ãããã ããã©ããThreadãããContinuationã®æ¹ãèªç¶ã ããã¨ããããThreadã¯Continuationã§å®è£ ã§ãããã®ã ãããªã¨ã³ããã£ããªThreadãå®è£ ããã®ã¯å²ã¨é¢åã ãã©ã
Continuationã¨Method/UnboundMethod/Procã®ä½¿ãæ¹ãè¿·ããã¨ã¯ãªãã¨æãããã¶ãããã£ã±ãContinuationã¯å¤ããè ãªãã ãªã
äºå
ã¨ã¾ãã以ä¸ã¯ç§ãæ¸ãããã£ããã¨ã§ã¯ãªãã¦ãåç½®ãã ã£ããã ãªãåç½®ãããµãããã§ãã¾ã£ãã®ã§ãã®ã¾ã¾å ¬éãã¾ãããã©ãã次åã¯ãããã®å¼ã³åºãå¯è½ãªãã¸ã§ã¯ããå¼ã£ã¦éãã§ã¿ãã