data Colour = Blue | Red data (Ord a) => Tree a = Null | Fork Colour a (Tree a) (Tree a) isEmpty Null = True isEmpty (Fork col x a b) = False minElem (Fork col x a b) = x deleteMin (Fork col x a b) = merge a b insert x a = merge (Fork Blue x Null Null) a merge a Null = a merge Null b = b merge a b | minElem a <= minElem b = join a b | otherwise = join b a join (Fork Blue x a b) c = Fork Red x (merge a c) b join (Fork Red x a b) c = Fork Blue x a (merge b c)How about object-oriented programming? Dynamic dispatch is preferred to if expressions. The above example can be written in Ruby as follows:
class Tree def insert(value) BlueFork.new(value, Null, Null).merge(self) end def merge(other) other._merge(self) end end Null = Tree.new def Null.empty? true end def Null.merge(other) other end def Null._merge(other) other end class Fork < Tree attr_reader :value, :left, :right def initialize(value, left, right) @value, @left, @right = value, left, right end def empty? false end def min_elem value end def delete_min merge(left, right) end def _merge(other) if min_elem <= other.min_elem self.join(other) else other.join(self) end end end class BlueFork < Fork def join(other) RedFork.new(value, left.merge(other), right) end end class RedFork < Fork def join(other) BlueFork.new(value, left, right.merge(other)) end endThis Ruby code is slightly more verbose than Haskell code, but join is well defined using dynamic dispatch. However, dynamic dispatch has some limitations:
- Ruby doesn't support multiple dispatch, so which method is invoked depends on only the receiver (in the above code, merge is implemented using double-dispatch techniques).
- Dynamic dispatch doesn't work on recursive data structure.
2 is more critical. For example. it's hard to implement balancing of red-black trees using dynamic dispatch.
Kazuki Tsujimoto has implemented pattern-match to support pattern matching in Ruby. Balancing of red-black trees can be implemented using pattern-match as follows:
Kazuki Tsujimoto has implemented pattern-match to support pattern matching in Ruby. Balancing of red-black trees can be implemented using pattern-match as follows:
Node = Struct.new(:left, :key, :right) class R < Node; end class B < Node; end def balance(left, key, right) match([left, key, right]) { with(_[R.(a, x, b), y, R.(c, z, d)]) { R[B[a, x, b], y, B[c, z, d]] } with(_[R.(R.(a, x, b), y, c), z, d]) { R[B[a, x, b], y, B[c, z, d]] } with(_[R.(a, x, R.(b, y, c)), z, d]) { R[B[a, x, b], y, B[c, z, d]] } with(_[a, x, R.(b, y, R.(c, z, d))]) { R[B[a, x, b], y, B[c, z, d]] } with(_[a, x, R.(R.(b, y, c), z, d)]) { R[B[a, x, b], y, B[c, z, d]] } with(_) { B[left, key, right] } } endWith Refinements, you don't need to use ugly .():
def balance(left, key, right) match([left, key, right]) { with(_[R[a, x, b], y, R[c, z, d]]) { R[B[a, x, b], y, B[c, z, d]] } with(_[R[R[a, x, b], y, c], z, d]) { R[B[a, x, b], y, B[c, z, d]] } with(_[R[a, x, R[b, y, c]], z, d]) { R[B[a, x, b], y, B[c, z, d]] } with(_[a, x, R[b, y, R[c, z, d]]]) { R[B[a, x, b], y, B[c, z, d]] } with(_[a, x, R[R[b, y, c], z, d]]) { R[B[a, x, b], y, B[c, z, d]] } with(_) { B[left, key, right] } } endBut balance is defined as a function, not as a method, and it doesn't look so Rubyish. Is there any object-oriented way to define balance?
No comments:
Post a Comment