The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Pattern matching over Ruby objects

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Eigen Class

Posts: 358
Nickname: eigenclass
Registered: Oct, 2005

Eigenclass is a hardcore Ruby blog.
Pattern matching over Ruby objects Posted: Nov 30, 2005 10:01 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Eigen Class.
Original Post: Pattern matching over Ruby objects
Feed Title: Eigenclass
Feed URL: http://feeds.feedburner.com/eigenclass
Feed Description: Ruby stuff --- trying to stay away from triviality.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Eigen Class
Latest Posts From Eigenclass

Advertisement

I've been writing about recursive structures and how to check whether two objects are similar for a while and I finally got to the code I wanted: recursion-safe "pattern matching" over Ruby objects. This begins to feel more useful, and if anything, it looks better.

Examples

We need a small class to show how this sort of pattern matching works, something as simple as this will do:

class Node
  attr_accessor :a, :b
  attr_reader :value
  def initialize(value, a,b)
    @a, @b = a, b
    @value = value
  end
end

Let's proceed to the first pattern: we will match any Node object and capture its instance variables (@value, @a and @b):

o = lambda{ Object.new }
pattern = Pattern.new do |p|
  Node.new(p.capture(:thevalue){ o[] }, 
           p.capture(:left){ o[] }, p.capture(:right){ o[] })
end

md = pattern.match Node.new("some value", nil, Node.new("something else", nil, nil))
md[:thevalue]                                      # => "some value"
md[:left]                                          # => nil
md[:right]                                         # => #<Node:0xb7ddaec0 @value="something else", @b=nil, @a=nil>
#regexpish
md[0]                                              # => #<Node:0xb7ddaeac @value="some value", @b=#<Node:0xb7ddaec0 @value="something else", @b=nil, @a=nil>, @a=nil>
md[1]                                              # => "some value"
md[2]                                              # => nil
md[3]                                              # => #<Node:0xb7ddaec0 @value="something else", @b=nil, @a=nil>
a, b, c = md.captures
a                                                  # => "some value"
b                                                  # => nil
c                                                  # => #<Node:0xb7ddaec0 @value="something else", @b=nil, @a=nil>

I can specify which values must be captured with

p.capture(:capture_name){  value to be matched and captured }

References to named captures

It's been really easy so far. Time to move to something a bit more interesting; I'll add a number of constraints:

  • the value held by the node must be a String
  • no left-child (nil)
  • the right-child must be a node with no children and with the same value as the parent

The last condition can be specified with a reference to a previous capture, e.g.

 Pattern.new{|p|   [p.capture(:val){ Object.new }, Object.new, p.capture(:val)] }

matches all arrays such that the first element is the same as the last. So let's do it:

pattern = Pattern.new do |p|
  Node.new(p.capture(:value){""}, nil, Node.new(p.capture(:value), nil, nil)) 
end
# matches a Node with a String value and whose 'right-child' is a node with the
# very same value and no children


md = pattern.match Node.new("some value", nil, Node.new("some value", nil, nil))
# different values (in the object_id sense), no match
md                                                 # => nil


s = "whatever"
md = pattern.match Node.new(s, nil, Node.new(s, nil, nil))
md[:value]                                         # => "whatever"

Recursive captures

No way I could forget recursive stuff, right? Here's another pattern, matching a node whose value is an array with three elements of classes String, Fixnum and String (or derived, as usual), and referring to itself:

pattern = Pattern.new do |p|
  p.capture(:all){ Node.new([p.capture(:l){ "" }, p.capture(:middle){ 1 }, ""], nil, p.capture(:all)) }
end

n = Node.new(["foo", 2, "whatever"], nil, Node.new(nil, nil, nil))
pattern.match(n)                                   # => nil
n.b = n
md = pattern.match(n)
md[:l]                                             # => "foo"
md[:middle]                                        # => 2


Read more...

Read: Pattern matching over Ruby objects

Topic: Rails question Previous Topic   Next Topic Topic: Forth is exciting

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use