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

Representations count

Tom Stuart
February 07, 2019

Representations count

In your grandparents’ attic you discover a mysterious old computer. You boot it up and discover it runs Ruby, but doesn’t support negative numbers! How can you implement negative numbers in an elegant way? We’ll explore two solutions and discover how important it is to pick the right representation.

Tom Stuart

February 07, 2019
Tweet

More Decks by Tom Stuart

Other Decks in Programming

Transcript

  1. $ irb >> 5 * 2 => 10 >> 5

    - 2 => 3 >> 2 - 5 ImpossibleNumberError (0 is the smallest number)
  2. class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

    end def +(other) end def -(other) end def *(other) end def <(other) end end
  3. $ irb >> require 'signed_number' => true >> def pos(number)

    SignedNumber.positive(number) end => :pos >> def neg(number) SignedNumber.negative(number) end => :neg
  4. >> pos(2) + pos(5) == pos(7) => true >> pos(2)

    - pos(5) == neg(3) => true >> (pos(9) - pos(3)) * (neg(4) + neg(3)) == neg(42) => true >> pos(2) * neg(3) < pos(4) + neg(9) => true
  5. class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

    end def +(other) end def -(other) end def *(other) end def <(other) end end
  6. 3 -

  7. class SignedNumber def self.positive(number) end def self.negative(number) end # …

    end protected attr_reader :sign, :size def initialize(sign, size) @sign, @size = sign, size end new(:negative, number) new(:positive, number)
  8. ==

  9. >> pos(1) == pos(1) => true >> neg(2) == neg(2)

    => true >> pos(1) == pos(2) => false >> pos(1) == neg(1) => false ✔ ✔ ✔ ✔
  10. +

  11. >> pos(3) + pos(1) == pos(4) => true >> neg(3)

    + neg(1) == neg(4) => true >> pos(3) + neg(1) == pos(2) => false >> pos(3) + neg(1) => #<SignedNumber @sign=:positive, @size=4> ✔ ✔ ✘
  12. else SignedNumber.new(sign, size - other.size) class SignedNumber # … def

    +(other) if sign == other.sign class SignedNumber # … def +(other) SignedNumber.new(sign, size + other.size) end # … end end
  13. >> pos(3) + neg(1) == pos(2) => true >> neg(3)

    + pos(1) == neg(2) => true >> pos(1) + neg(3) == neg(2) ImpossibleNumberError (0 is the smallest number) ✔ ✔
  14. else SignedNumber.new( sign == :positive ? :negative : :positive, other.size

    - size ) end end end # … end class SignedNumber # … def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else SignedNumber.new(sign, size - other.size) if size >= other.size
  15. >> pos(1) + neg(3) == neg(2) => true >> neg(1)

    + pos(3) == pos(2) => true >> pos(3) + neg(3) == neg(3) + pos(3) => false >> pos(3) + neg(3) => #<SignedNumber @sign=:positive, @size=0> >> neg(3) + pos(3) => #<SignedNumber @sign=:negative, @size=0> >> pos(0) == neg(0) => false ✔ ✘ ✘ ✔
  16. class SignedNumber # … def ==(other) end # … end

    ( || size.zero?) && size == other.size sign == other.sign
  17. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  18. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other)
  19. )a end end end def -(other) if sign == other.sign

    if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size)
  20. other.size - size )b end else SignedNumber.new(sign, size + other.size)

    end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size
  21. SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end

    end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  22. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )a end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size )b end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  23. class SignedNumber # … def ==(other) (sign == other.sign ||

    size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end
  24. class SignedNumber def self.positive(number) end def self.negative(number) end def ==(other)

    end def +(other) end def -(other) end def *(other) end def <(other) end end
  25. 3 -

  26. +2

  27. -2

  28. +5

  29. +1

  30. protected attr_reader :left, :right def initialize(left, right) @left, @right =

    left, right end end class SignedNumber def self.positive(number) end def self.negative(number) end # … new(number, 0) new(0, number)
  31. ==

  32. +2 = +2 +2 = +3 +3 = +2 left

    right right left + +
  33. left + other.right == other.left + right end # …

    end class SignedNumber # … def ==(other)
  34. <

  35. +2 < +2 +2 < +3 +3 < +2 left

    right right left + +
  36. other.left + right < left + other.right end # …

    end class SignedNumber # … def <(other)
  37. +

  38. -

  39. *

  40. =

  41. - =

  42. - =

  43. SignedNumber.new( left * other.right + other.left * right, left *

    other.left + right * other.right ) end # … end class SignedNumber # … def *(other)
  44. class SignedNumber # … def ==(other) left + other.right ==

    other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end
  45. class SignedNumber # … def ==(other) left + other.right ==

    other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end
  46. class SignedNumber # … def ==(other) left + other.right ==

    other.left + right end def +(other) SignedNumber.new(left + other.left, right + other.right) end def -(other) SignedNumber.new(left + other.right, other.left + right) end def *(other) SignedNumber.new( left * other.right + other.left * right, left * other.left + right * other.right ) end def <(other) other.left + right < left + other.right end # … end class SignedNumber # … def ==(other) (sign == other.sign || size.zero?) && size == other.size end def +(other) if sign == other.sign SignedNumber.new(sign, size + other.size) else if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end end end def -(other) if sign == other.sign if size >= other.size SignedNumber.new(sign, size - other.size) else SignedNumber.new( sign == :positive ? :negative : :positive, other.size - size ) end else SignedNumber.new(sign, size + other.size) end end def *(other) if sign == other.sign SignedNumber.new(:positive, size * other.size) else SignedNumber.new(:negative, size * other.size) end end def <(other) if sign == other.sign if sign == :positive size < other.size else other.size < size end else sign == :negative && !(size.zero? && other.size.zero?) end end # … end