Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Re: Pattern Matching in Ruby

k_tsj
May 28, 2016

Re: Pattern Matching in Ruby

k_tsj

May 28, 2016
Tweet

More Decks by k_tsj

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ • Twitter: @k_tsj • GitHub:k-tsj • E-mail: [email protected]

    Rubyίϛολ • power_assert gem։ൃऀ • TRICK 2015 Matz Lisp Awardड৆
  2. Matz Lisp Award '(#|'.b;module Scheme;T,R,I,C,K=Struct.new(:a,:d,:o){include Enumerable# |#)#| def initialize x,y=(),o=0.!;super

    c(x),c(y),o;K.empty?&&(R.delete a;R.delete d R<<self)end;def-@;T[:-,self]end;alias == equal?;def call i;Scheme.t self,i end def to_a;[T[:*,self]]end;def c o;Array===o ?Scheme.l(o):o end;def each&b;b.(a) d&&d.each(&b)end},[],e=Struct.new(:t,:u){def[]=k,v;t[k]=v end;def[]i;t.fetch(i ){u[i]}end},e[_h={}],[];def Object.const_missing i;i end;class::Fixnum;undef[] def[]i;T[self,T[i],1]end end; module A;def call *a;a.empty?? T[self]: Scheme.t(self,a[0])end;def-@; T[:-,T[self]] end end;class::Array;def call a;T[self,T===a&&a.o ? a: T[a],1]end end;class::Symbol;def*i;T[ self,T[:*,T[i]]]end;include A ;def -i;Symbol===i ?:"#{self}-#{i}": T[self,T[:-,T[i]]]end end;at_exit{ R.drop(K[0]=$0==__FILE__ ?0:2).each{ |l|v l}};B=TOPLEVEL_BINDING;class:: String;include A end;m=eval('self', B);def m.method_missing i,*s;a,=s;s .empty?? i:T[i,T===a&&a.o ? a:T[a] ] end;class<<self;def e f,n;f.map{ |i|v i,n}[-1]end;def r x,f,n;->*a{ e x.d.d,I[f ?Hash[f.zip a]:{},n]} end; def l v;v.reverse.inject(() ){|a,i|T[i,a]}end;def t s,i;T[s, Array===i ? T[i[0]]:T===i&&i.o ? i:T[i],1]end;def v x,n=C; case x when T;case x.a when :LAMBDA; r x,x.d.a,n when :LET;e={};x. d.a.each{|i|e[i.a]=v i.d.a,n}; e x.d.d,I[e,n]when :IF;v(v(x .d. a,n)?x.d.d.a: (y=x.d.d.d)?y.a : (),n)when :DEFINE;Symbol. ===(u= x.d.a)?(n[u]=v x.d.d.a,n): ( n.[]=(u.a,r(x,u.d,n)))when :QUOTE;d =x.d.a;d==false ?:FALSE:(d== true)?:TRUE:d when :COND ; while x= x.d; (break e x.a.d, n if :ELSE== x.a.a||v(x .a.a,n))end; else f,*r=x.map {|i| v i,n}; f.call(*r);end when Symbol;n .[] x else x end end end;%w(CONS a,b T[a,b] PAIR? o T===o - *s s.inject:- LENGTH l l.to_a.size APPLY f,*n,s f.(*(n+(s||[]).map.to_a)) SYMBOL? o Symbol===o READ * t=""*1;begin;gets&&eval(t<<($_!=$/?$_:x),B);rescue(Object);retry;end MAP p,l l(l.map(&p)) NUMBER? o Numeric===o STRING? o String===o NOT o 0.!.==o ERROR *s fail(s*"\s") EOF-OBJECT? o o==() DISPLAY o puts(o) SET-CDR! p,o p.d=o SET-CAR! p,o p.a=o NULL? o o==() EQ? a,b a.equal?b * *s s.inject:* MIN *s s.min LIST *s l(s) CAR p p.a CDR p p.d).each_slice(3){|t|eval'_h[:"%s"]=->%s{%s}'%t}end;# |# (DEFINE (FACT N) . ( (IF (EQ? N . (1)) . ( 1 [* N . ((FACT (- N . (1))))])))) (DISPLAY (FACT 6))
  3. ύλʔϯϚονͱ͸ • ύλʔϯϚον͋Γ • ύλʔϯϚονͳ͠ match(l, v, r) { with(_[R.(R.(a,

    x, b), y, c), z, d]) { R[B[a, x, b], y, B[c, z, d]] } } case when l.kind_of?(R) && l.l.kind_of?(R); R[B[l.l.l, l.l.v, l.l.r], l.v, B[l.r, v, r]] end
  4. ೚ҙͷந৅σʔλܕͱͷϚον • StringΛEMailͱΈͳͯ͠Ϛον match("invalid e-mail address") { with(EMail.("foo", domain)) {

    domain } } #=> NoMatchingPatternError match("[email protected]") { with(EMail.("foo", domain)) { domain } end #=> NoMatchingPatternError
  5. ܁Γฦ͠ match([[0, 1], [2, 3]]) { with(_[_[a, b], ___]) {

    [a, b] #=> [[0, 2], [1, 3]] } # or with(_[*_[a, b]]) { [a, b] #=> [[0, 2], [1, 3]] } }
  6. ར༻ྫ - power_assert gem - • දࣔҐஔ(ܻ)Λಛఆ͢ΔͨΊʹɺRubyͷίʔ υΛRipperͰύʔε # assert

    { [0, 1, 2].find {|i| i.odd? } == 3 } [:program, [[:method_add_block, [:method_add_arg, [:fcall, [:@ident, "assert", [1, 0]]], []], [:brace_block, nil, [[:binary, [:method_add_block, [:call, [:array,ɹ[[:@int, "0", [1, 10]], [:@int, "1", [1, 13]], [:@int, "2", [1, 16]]]], :".", [:@ident, "find", [1, 19]]], [:brace_block, [:block_var, [:params, [[:@ident, "i", [1, 26]]], nil, nil, nil, nil, nil, nil], false], [[:call, [:var_ref, [:@ident, "i", [1, 29]]], :”.", [:@ident, "odd?", [1, 31]]]]]], :==, [:@int, "3", [1, 41]]]]]]]]
  7. ར༻ྫ - power_assert gem - • ॳظ࣮૷Ͱpattern-matchΛར༻ match(sexp) do with(_[:program,

    _[_[:method_add_block, _[:method_add_arg, _[:fcall, _[:@ident, assertion_method.to_s, _]], _], _[Or(:brace_block, :do_block), _, ss]]]]) do ss.flat_map {|s| extract_idents(s) } end ...
  8. ར༻ྫ - ICFP Programming Contest 2014 - • GitHub:NaCl-Ltd/yarunee-2014 •

    ίϯύΠϥΛ࣮૷ def compile(e, env, is_tail = false) match(e){ ... [ [:+, :ADD], [:-, :SUB], [:*, :MUL], [:/, :DIV], [:"=", :CEQ], [:>, :CGT], [:>=, :CGTE], ].each do |sym, opname| with(_[sym, ex, ey]){ compile(ex, env) + compile(ey, env) + Gcc.new([Op[opname]]) } end
  9. ར༻ྫ - open-uri - • πϦʔҎ֎ͷར༻ྫ • ݱࡏͷ࣮૷ • ύλʔϯϚονΛ࢖ͬͨ৔߹

    mode, _, rest = OpenURI.scan_open_optional_arguments(*rest) options = rest.shift if !rest.empty? && Hash === rest.first match(OpenURI.scan_open_optional_arguments(*rest)) do with(_[mode, _, _[options & Hash]]) do end end
  10. ઃܭͷϙΠϯτ • GitHub:hayeah/matchmaker • GitHub:jdantonio/functional-ruby c = Case.new { of(1)

    { :a } of(2) { :b } of(3) # { true } by default } c.match(1) defn(:greet, _) do |name| "Hello, #{name}!" end
  11. ઃܭͷϙΠϯτ • GitHub:todesking/patm • GitHub:egison/egison-ruby class A define_matcher :match1 do|r|

    p = Patm r.on [:x, p._1, p._2] do|m| [m._1, m._2] end end end A.new.match1([:x, 1, 2]) match_all([1, 2, 3]) do with(List.(*_hs, _x, *_ts)) do [hs, x, ts] end end
  12. ϚονॲཧͷશମͷྲྀΕ • Ϛον༻ͷπϦʔͷߏங • ݟͨ໨ॏࢹͷจ๏ཁૉͷར༻ • Refinements • ϝλϓϩάϥϛϯά •

    πϦʔͱΦϒδΣΫτͷϚον • callcc • Ϛον݁Ռͷ”ม਺”΁ͷ୅ೖ • ϝλϓϩάϥϛϯά
  13. ݟͨ໨ॏࢹͷจ๏ཁૉͷར༻ • splat(*) • to_aϝιουͰಈ࡞ΛΧελϚΠζ • “*a” Λ “a, ___”

    ಉ౳ʹѻ͏Α͏ʹ͍ͯ͠Δ class C def to_a; [0, 1]; end end p(*C.new) #=> [0, 1] class Pattern def to_a [self, PatternQuantifier.new(0, true)] end end
  14. ݟͨ໨ॏࢹͷจ๏ཁૉͷར༻ • ಥ͖٧Ίͨྫ • Matz Lisp Award (DEFINE (FACT N)

    . ( (IF (EQ? N . (1)) . ( 1 [* N . ((FACT (- N . (1))))])))) define(fact(n).call(IF(eq? n.call(1)).call(1[*n.call(fact(-n.call(1)))])))
  15. Refinements • ରԠ͸೉͘͠ͳ͍ • refineΛఆٛ + using͸ݺ΂Δ͚࣌ͩݺͿ • EOLͷ΋ͷ΋ؚΊͯ෯޿͍؀ڥͰಈ࡞ •

    1.9.3/2.0.0-p648/2.1.10/2.2.5/2.3 module def refine(klass, &blk) klass.class_eval(&blk) end end using PatternMatch if respond_to?(:using, true)
  16. πϦʔͱΦϒδΣΫτͷϚον %FDPOTUSVDUPS "SSBZ %FDPOTUSVDUPS "SSBZ 7BSJBCMF B 2VBOUJpFS 7BMVF 

    "SSBZ "SSBZ    match([[2, 1], 0]) { with(_[_[a, ___], 0]) {} }
  17. callccಋೖͷܦҢ • ౰ॳ͸Rubyͷଟॏ୅ ೖͱ߹Θͤͯύλʔϯ ଆͷ”*”͸1͚ͭͩΛ૝ ఆ • πϦʔΛ୯७ʹ࠶ى తʹτϥόʔεͯ͠ ॲཧ͢ΔΑ͏ʹ࣮૷

    • ਖ਼نදݱͷαϙʔτʹ ΑΓͦͷલఏ่͕Εͨ match([[0, 0], [0, 1]]) { with(_[_[*a, *_], _[*b, *_]], guard { a == b }) { a #=> [0] } }
  18. callccಋೖͷܦҢ • ར༻ස౓Λߟ͑ͯ ָʹ࣮૷Ͱ͖Δ͜ ͱΛॏࢹ • ϝΠϯϩδοΫͷ มߋෆཁͰޙ෇͚ Ͱ࣮૷Ͱ͖ͨ •

    callccศར class Pattern module Backtrackable def match(vals) matched = super if root? and not matched and not choice_points.empty? restore_choice_point end matched end def repeating_match(vals, is_greedy) super do |vs, rest| cont = nil if callcc {|c| cont = c; yield vs, rest } save_choice_point(cont) true else false end end end end ... prepend Backtrackable end