ãé¢æ°åRubyãã¨ããç (2) - é¢æ°åæ Proc#compose
æ¬è¨äºã¯ãRubyãæ¸ãã«ããã£ã¦ãããã«ãããã¯ãæ¸ããã«ãã¾ããããã追æ±ããã誰å¾ãªé£è¼ã§ããã
注æç¹ã¨ãã¦ããã®è¨äºã¯ãããã°ã©ãå¨äºç ã®ã²ã¨ã¤ã§ãããã©ã ãçå群(λ-Syndrome)ãã«ç½¹æ£ããæ£è ã«Rubyãæ¸ãããã¨ãã©ããªããããã¨ã«ãªããå®ä¾ã示ãããã®ã§ãããããã¦ãã®ãããªããã°ã©ãã³ã°ã¹ã¿ã¤ã«ãæ¨å¥¨ãããã®ã§ã¯ãªãã
ãªãé¢æ°åæ?
ã¾ãããªãé¢æ°åæãå¿ è¦ãããã®ã¢ããã¼ã·ã§ã³ã示ãã
前回ãåãªãã¡ã½ããå¼ã³åºããä¸å¼æ°ã®é¢æ°é©ç¨ã®ããã ãã«ãããã¯ãè¨è¿°ããå¿
è¦ã¯ãªããã¨ãã話ãããã
ã ãã以ä¸ã®ãããªSymbolã®Arrayããããåè¦ç´ ãto_sããä¸ã§upcaseãããå ´åã¯ã©ããããï¼
irb(main):003:0> arr = [:user, :entry, :article, :comment, :category] => [:user, :entry, :article, :comment, :category] irb(main):002:0> arr.map{|_| _.to_s.upcase } => ["USER", "ENTRY", "ARTICLE", "COMMENT", "CATEGORY"] irb(main):003:0> arr.map(&:to_s).map(&:upcase) => ["USER", "ENTRY", "ARTICLE", "COMMENT", "CATEGORY"]
ä¸è¨ã®ããã«ãç¡å¿µã«ããããã¯ãæ¸ãããmapã2åéãããããªãããããã¯ã¨ãæ¸ããæç¹ã§è² ã確å®ãªã®ã§ä½ã¨ãæ»ä½ã
ãã®åé¡ã解決ããããã«ãé¢æ°åæãå¿ è¦ãªã®ã ã
é¢æ°åæã®å®è£
Rubyã§ã¯ãæ¨æºã§é¢æ°åæãã§ããªãã®ã§ãèªåã§å®è£ ããå¿ è¦ãããã ãRubyで関数合成したいなら関数型言語使ってろããªãã¦ããæè¦ãããã®ã ããé¢æ°ããã¡ã¼ã¹ãã¯ã©ã¹ã«æ±ããã®ã«é¢æ°åæããªããªãã¦ãã£ãããªããããã
ããããããã¦ããã¨ãé¢æ°åæã¨ã¯ã é¢æ°gã¨é¢æ°fãããg(f(x))ã¨ããé¢æ°hãæ°ãã«ä½ãåºããã¨ã ã
(g â f)(x) = g(f(x))
é¢æ°gã¨é¢æ°fã®åæé¢æ°g â fã«å¼æ°xã渡ããçµæã¯ãé¢æ°gã«f(x)ã®çµæã渡ãããã®ã¨çãããã¤ã¾ãããã®ãããªæä½ã§ããã
irb(main):004:0> f = lambda{|x| x + 1 } => #<Proc:0x007ff6ed91e358@(irb):4 (lambda)> irb(main):005:0> g = lambda{|x| x * x } => #<Proc:0x007ff6ed9123f0@(irb):5 (lambda)> irb(main):008:0> h = lambda{|x| g.(f.(x)) } => #<Proc:0x007ff6ed905ec0@(irb):8 (lambda)>
Procã«å¯¾ãã¦ãé¢æ°gãå¼æ°ã«åã£ã¦ãèªèº«ã¨ã®åæé¢æ°ãè¿ãã¡ã½ããcomposeã以ä¸ã®ããã«å®ç¾©ããã
module ComposableFunction def compose(g) lambda{|*args| self.to_proc.call(g.to_proc.call(*args)) } end def >>(g) g << self end def self.included(klass) klass.send(:alias_method, :<<, :compose) end end [Proc, Method, Symbol].each do |klass| klass.send(:include, ComposableFunction) end
"<<"ã¯composeã®aliasã§ã">>"ã¯å¼æ°ã®é åºãå
¥ãæ¿ããcomposeã ã">>"ã¨"<<"ã¯é¢æ°é©ç¨ã®æµãã示ãã¦ãããããã¯Groovyç±æ¥ã (Scalazã§ã¯>>>ãããã)
gã¨fã®åæé¢æ°ã¯ããé¢æ°fã®çµæãé¢æ°gã«æ¸¡ãé¢æ°ãã¨è¨ããã®ã§ããã®å®ç¾©éãã«"f >> g"ã¨æ¸ãã¨gã¨fã®åæé¢æ°ãå¾ãããããã«ãªã£ã¦ããã
åé ã®ä¾ã§åºããããto_sãã¦ããupcaseãããã«ã¯ã:to_s.to_procã§å¾ãããProcã¨ã:upcase.to_procã§å¾ãããProcã">>"ã§åæããã°ããã
irb(main):067:0> f = :to_s.to_proc => #<Proc:0x007ff6ed924848> irb(main):068:0> g = :upcase.to_proc => #<Proc:0x007ff6ed924690> irb(main):069:0> h = f >> g => #<Proc:0x007ff6ec020360@(irb):47 (lambda)> irb(main):070:0> h.(:abc) => "ABC"
ããã¦ããã®ããã«ãã¦å¾ãåæé¢æ°hããarrã®mapã¡ã½ããã«æ¸¡ãã°ãmapãéãããã¨ããªãããããã¯ãæ¸ããã¨ããªãç®çãéæã§ããã
irb(main):072:0> arr.map(&h) => ["USER", "ENTRY", "ARTICLE", "COMMENT", "CATEGORY"] irb(main):073:0> arr.map(&:to_s >> :upcase) => ["USER", "ENTRY", "ARTICLE", "COMMENT", "CATEGORY"]
äºãåæããé¢æ°ãå¤æ°ã«å
¥ãã¦ããã¦ãããããmapã«æ¸¡ãéã«map(&:to_s >> :upcase)ã®ããã«åæãã¦ããããSymbolã«ãComposablefunctionãincludeããã¦ããã®ã§ãåæã«ããã£ã¦to_procãå¼ã³åºãã¦ãã¡ãã¡å¤æããªãã¦ããããããªä»æãã«ãã¦ããã
æ¬æ¥ã¯ãmap{|sym| sym.to_s.upcase} ã¨æ¸ãã¦ããå¦çãmap(&:to_s >> :upcase)ã«å¤æã§ãããããã¯ãã¡ã½ãããã§ã¼ã³ãè¡ããããªãããã¯ã¯Symbol#to_procã¨é¢æ°åæã«ããç½®æå¯è½ã§ãããã¨ã示ãã¦ããã
Object#methodã¨çµã¿åããããã¨ãã§ããã
irb(main):075:0> arr.each(&:to_s >> :upcase >> method(:puts)) USER ENTRY ARTICLE COMMENT CATEGORY => [:user, :entry, :article, :comment, :category]
ä½è«ã ããarr.map(f).map(g)ã¨arr.map(g compose f) ãçãããªãã®ã¯ãååãè¨ã£ã¦ã¯ãããªããã®æ³å(ï¾ï¾ï½¬ï½°ï½°ï½°ï½¯!!)ãæãåºãããããã£ã¦ãåæãããé¢æ°ãå¯ä½ç¨ãä¼´ãå ´åã¯æ³¨æããããå¯ä½ç¨ã¯æ»
ã¹ã°ãã( ;ï¾ç¿ï¾)ï¾ï½¼Î£ ï¾ï½¨ï¾ï½·ï¾ï½¨ï½¨ï½°ï½°ï½¯!!!
次åã¯ãã«ãªã¼åã«ã¤ãã¦æ¸ã( êªâêª)