Representations count

Cd9b247e4507fed75312e9a42070125d?s=47 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.

Cd9b247e4507fed75312e9a42070125d?s=128

Tom Stuart

February 07, 2019
Tweet

Transcript

  1. 3.
  2. 4.

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

    - 2 => 3 >> 2 - 5 ImpossibleNumberError (0 is the smallest number)
  3. 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
  4. 6.

    $ irb >> require 'signed_number' => true >> def pos(number)

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

    >> 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
  6. 9.

    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
  7. 10.

    3 -

  8. 12.

    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)
  9. 13.

    ==

  10. 15.

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

    => true >> pos(1) == pos(2) => false >> pos(1) == neg(1) => false ✔ ✔ ✔ ✔
  11. 16.

    +

  12. 18.

    >> 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> ✔ ✔ ✘
  13. 19.

    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
  14. 20.

    >> 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) ✔ ✔
  15. 21.

    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
  16. 22.

    >> 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 ✔ ✘ ✘ ✔
  17. 23.

    class SignedNumber # … def ==(other) end # … end

    ( || size.zero?) && size == other.size sign == other.sign
  18. 25.
  19. 26.

    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
  20. 27.

    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)
  21. 28.

    )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)
  22. 29.

    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
  23. 30.

    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. 31.

    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
  25. 32.

    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
  26. 35.

    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
  27. 36.

    3 -

  28. 37.
  29. 41.
  30. 42.
  31. 48.

    +2

  32. 49.
  33. 50.
  34. 51.

    -2

  35. 52.
  36. 53.
  37. 54.

    +5

  38. 56.
  39. 57.
  40. 58.
  41. 59.

    +1

  42. 60.

    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)
  43. 61.

    ==

  44. 62.
  45. 63.
  46. 64.
  47. 65.
  48. 66.
  49. 67.
  50. 68.
  51. 69.
  52. 70.
  53. 72.

    +2 = +2 +2 = +3 +3 = +2 left

    right right left + +
  54. 73.

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

    end class SignedNumber # … def ==(other)
  55. 74.

  56. 75.

    <

  57. 77.

    +2 < +2 +2 < +3 +3 < +2 left

    right right left + +
  58. 78.

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

    end class SignedNumber # … def <(other)
  59. 79.

  60. 80.

    +

  61. 81.
  62. 82.
  63. 83.
  64. 86.

  65. 87.

    -

  66. 88.
  67. 89.
  68. 90.
  69. 93.

  70. 94.

    *

  71. 95.
  72. 96.
  73. 97.
  74. 98.
  75. 99.
  76. 100.
  77. 101.

    =

  78. 102.

    - =

  79. 103.
  80. 104.
  81. 107.

    - =

  82. 109.

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

    other.left + right * other.right ) end # … end class SignedNumber # … def *(other)
  83. 110.

  84. 111.

    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
  85. 112.

    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
  86. 113.

    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
  87. 114.
  88. 118.