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

Solving algorithmic problems in Ruby

Solving algorithmic problems in Ruby

In this presentation, I introduced Ruby as a great programming language for solving algorithmic problems.

This talk highlights:
- Tight feedback loop through irb.
- Rich and intuitive built-in libraries.
- Simple and expressive syntax.

Thai Pangsakulyanont

May 30, 2018
Tweet

More Decks by Thai Pangsakulyanont

Other Decks in Programming

Transcript

  1. Thai Pangsakulyanont
    1
    Solving algorithmic
    problems in Ruby
    Ruby Wednesday #4

    View full-size slide

  2. About Me
    Slide № 2
    @dtinth
    Thai Pangsakulyanont

    View full-size slide

  3. About Me
    Slide № 3
    JavaScript developer
    Thai Pangsakulyanont
    I am a…
    (speaking in a Ruby meetup)

    View full-size slide

  4. About Me
    Slide № 4
    Front-end architect @ Taskworld
    Thai Pangsakulyanont

    View full-size slide

  5. About Me
    Slide № 5
    Use Ruby a lot
    Thai Pangsakulyanont
    But in my day-to-day work and in daily life, I find myself using Ruby a lot!

    View full-size slide

  6. Slide № 6
    Today:
    Using Ruby to solve problems

    View full-size slide

  7. Slide № 7
    Today:
    Using Ruby to solve problems
    - Competitive programming
    (A competition where you go and solve algorithm problems
    and win some internet points, and sometimes, some cash)

    View full-size slide

  8. Slide № 8
    Today:
    Using Ruby to solve problems
    - Competitive programming
    - Practical, daily life stuff

    View full-size slide

  9. Slide № 9
    Advent of Code
    Last year, I’ve been playing
    with this thing called…

    View full-size slide

  10. Slide № 10
    Advent of Code
    Yearly programming
    competition

    View full-size slide

  11. Slide № 11
    Advent of Code
    Yearly programming
    competition
    December 1~25

    View full-size slide

  12. Slide № 12
    Advent of Code
    Yearly programming
    competition
    December 1~25
    Each day @ 12:00,
    a new problem
    12:00pm Thailand Time

    View full-size slide

  13. Slide № 13
    Advent of Code
    Each problem,
    two puzzles
    When you solve the first puzzle
    the second puzzle will be unlocked.

    View full-size slide

  14. Slide № 14
    Advent of Code
    Each problem,
    two puzzles
    Each puzzle solved
    gives you one star

    View full-size slide

  15. Slide № 15
    Goal
    Collect 50 stars
    If you want to flex your
    problem-solving muscle,
    I really recommend trying it.
    Most problems are not too hard.
    Although the event is already over,
    you can still collect stars.

    View full-size slide

  16. Slide № 16
    Goal
    Collect 50 stars
    Competition
    But there’s also this
    competition part, and it’s a
    really fierce competition.

    View full-size slide

  17. Slide № 17
    Goal
    Collect 50 stars
    Competition
    Let’s look at the leaderboard.

    View full-size slide

  18. Slide № 18
    Goal
    Collect 50 stars
    Competition
    Here’s how it works…

    View full-size slide

  19. Slide № 19
    Goal
    Collect 50 stars
    Competition
    • The first person to collect

    each star gets 100 pts.

    View full-size slide

  20. Slide № 20
    Goal
    Collect 50 stars
    Competition
    • The first person to collect

    each star gets 100 pts.
    • The second person, 99 pts
    • (…)

    View full-size slide

  21. Slide № 21
    Goal
    Collect 50 stars
    Competition
    • The first person to collect

    each star gets 100 pts.
    • The second person, 99 pts
    • (…)
    • The 100th person, 1 pt.
    After this, you can collect stars but you won’t get any point.

    View full-size slide

  22. Slide № 22
    Now, let’s go ahead and look at
    the first day’s problem.

    View full-size slide

  23. Slide № 23

    View full-size slide

  24. Slide № 24
    When you enter, you will see a wall of text that you have to quickly read through.

    View full-size slide

  25. Slide № 25
    Since it’s the first day, here it explains the rules of this game.

    View full-size slide

  26. Slide № 26
    Scrolling down, you’ll encounter…

    View full-size slide

  27. Slide № 27
    The problem statement…

    View full-size slide

  28. Slide № 28
    …and some example cases.
    Before we dive in, let’s look at the leaderboard for this problem.

    View full-size slide

  29. Slide № 29
    As you can see, if you don’t solve this problem in
    3 minutes 47 seconds, you’re not going to win any point.

    View full-size slide

  30. Slide № 30
    Now let’s look at each example.

    View full-size slide

  31. Slide № 31

    View full-size slide

  32. Slide № 32
    1122
    I’ll explain how to solve this puzzle quickly.
    Basically, you have to look for…

    View full-size slide

  33. Slide № 33
    1122
    +1 +2
    …the digits that match the next digit.
    Sum them all to obtain the puzzle’s answer.

    View full-size slide

  34. Slide № 34
    1122
    +1 +2
    Solution: 3

    View full-size slide

  35. Slide № 35
    1111

    View full-size slide

  36. Slide № 36
    1111
    +1 +1 +1
    +1
    As you can see, it also wraps around.

    View full-size slide

  37. Slide № 37
    1111
    +1
    Solution: 4
    +1 +1
    +1

    View full-size slide

  38. Slide № 38
    1234 How about this one?

    View full-size slide

  39. Slide № 39
    1234 Solution: 0

    View full-size slide

  40. Slide № 40
    91212129 And this one?

    View full-size slide

  41. Slide № 41
    91212129
    +9

    View full-size slide

  42. Slide № 42
    91212129 Solution: 9
    +9

    View full-size slide

  43. Slide № 43
    Devising a solution
    After understanding the problem, now it’s time to devise a solution.

    View full-size slide

  44. Slide № 44
    1223334551
    Solution: 0
    The most obvious way to solve this is to go through each digit, …

    View full-size slide

  45. Slide № 45
    1223334551
    Solution: 0
    …comparing it to the next…

    View full-size slide

  46. Slide № 46
    1223334551
    Solution: 0

    View full-size slide

  47. Slide № 47
    1223334551
    Solution: 0

    View full-size slide

  48. Slide № 48
    1223334551
    +2
    Solution: 2
    …and if they match, add it to the accumulator…

    View full-size slide

  49. Slide № 49
    1223334551
    Solution: 2

    View full-size slide

  50. Slide № 50
    1223334551
    Solution: 2
    …and keep repeating…

    View full-size slide

  51. Slide № 51
    1223334551
    +3
    Solution: 5

    View full-size slide

  52. Slide № 52
    1223334551
    +3
    Solution: 8

    View full-size slide

  53. Slide № 53
    1223334551
    +5
    Solution: 13

    View full-size slide

  54. Slide № 54
    1223334551
    +1
    Solution: 14
    …until you reached the end.

    View full-size slide

  55. Slide № 55
    1223334551
    Solution: 14 The answer will be in
    the accumulator.

    View full-size slide

  56. Slide № 56
    Let’s quickly write some code for this!

    View full-size slide

  57. Slide № 57
    input = "1223334551"

    View full-size slide

  58. Slide № 58
    input = "1223334551"
    solution = 0

    View full-size slide

  59. Slide № 59
    input = "1223334551"
    solution = 0
    (0...input.length)

    View full-size slide

  60. Slide № 60
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    end

    View full-size slide

  61. Slide № 61
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1)]
    end
    end

    View full-size slide

  62. Slide № 62
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1) % input.length]
    end
    end
    wrapping around

    View full-size slide

  63. Slide № 63
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1) % input.length]
    solution += input[i]
    end
    end

    View full-size slide

  64. Slide № 64
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1) % input.length]
    solution += input[i].to_i
    end
    end
    don’t forget to convert to int

    View full-size slide

  65. Slide № 65
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1) % input.length]
    solution += input[i].to_i
    end
    end
    puts solution At the end of the loop, the solution variable
    contains the answer. Let’s print it.

    View full-size slide

  66. Slide № 66
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1) % input.length]
    solution += input[i].to_i
    end
    end
    puts solution
    (Many people would write code like this, but in their favorite
    language)

    View full-size slide

  67. Slide № 67
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1) % input.length]
    solution += input[i].to_i
    end
    end
    puts solution
    Slow feedback loop.
    This works correctly, but the more I learn Ruby, I realize this is an inefficient
    way of solving the problem. The feedback loop is too slow. You have to write
    the whole program until you can check if it’s correct or not.

    View full-size slide

  68. Slide № 68
    input = "1223334551"
    solution = 0
    (0...input.length).each do |i|
    if input[i] == input[(i + 1) % input.length]
    solution += input[i].to_i
    end
    end
    puts solution
    Slow feedback loop.
    Incorrect result
    Error
    If you made a mistake, you’ll only know it when you run the program.
    Then you have to figure out what’s wrong. Then fix it. Then run it again.

    View full-size slide

  69. Slide № 69
    Solving the problem again,
    but with faster feedback loop

    View full-size slide

  70. Slide № 70
    irb
    Interactive Ruby
    Interactive Ruby runs your code line-by-line,
    giving you immediate feedback after each statement.

    View full-size slide

  71. Slide № 71
    $ irb

    View full-size slide

  72. Slide № 72
    $ irb
    2.4.1 :001 >
    Running `irb` puts me into the Ruby shell.

    View full-size slide

  73. Slide № 73
    $ irb
    2.4.1 :001 > input = "1223334551"
    First, I store the input in Ruby’s memory.

    View full-size slide

  74. Slide № 74
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 >

    View full-size slide

  75. Slide № 75
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    Let’s try breaking it down by character.

    View full-size slide

  76. Slide № 76
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 >

    View full-size slide

  77. Slide № 77
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > input.chars
    Pressing [Up] recalls the last line.

    View full-size slide

  78. Slide № 78
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > input
    I don’t want to deal with wrapping around, so…

    View full-size slide

  79. Slide № 79
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input
    I’m just gonna take the first digit…

    View full-size slide

  80. Slide № 80
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    …and append it to the end of the string.

    View full-size slide

  81. Slide № 81
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 >

    View full-size slide

  82. Slide № 82
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 > (input + input[0])

    View full-size slide

  83. Slide № 83
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 > (input + input[0]).chars
    Let’s break it down into characters again.

    View full-size slide

  84. Slide № 84
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 > (input + input[0]).chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1", "1"]
    2.4.1 :005 >

    View full-size slide

  85. Slide № 85
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 > (input + input[0]).chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1", "1"]
    2.4.1 :005 > Now, I have an array of string.
    I think it’s easier for me to work with array of numbers instead.

    View full-size slide

  86. Slide № 86
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 > (input + input[0]).chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1", "1"]
    2.4.1 :005 > (input + input[0]).chars

    View full-size slide

  87. Slide № 87
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 > (input + input[0]).chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1", "1"]
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    So I’m gonna map over this array, converting each element into int.

    View full-size slide

  88. Slide № 88
    $ irb
    2.4.1 :001 > input = "1223334551"
    => "1223334551"
    2.4.1 :002 > input.chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1"]
    2.4.1 :003 > (input + input[0])
    => "12233345511"
    2.4.1 :004 > (input + input[0]).chars
    => ["1", "2", "2", "3", "3", "3", "4", "5", "5", "1", "1"]
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]

    View full-size slide

  89. Slide № 89
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 >

    View full-size slide

  90. Slide № 90
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i)
    Now, I’m going to use…

    View full-size slide

  91. Slide № 91
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    …the `each_cons` method.

    View full-size slide

  92. Slide № 92
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > It returns an Enumerator that lets me
    iterate through each 2 consecutive digits.

    View full-size slide

  93. Slide № 93
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    To see how the iteration looks like…

    View full-size slide

  94. Slide № 94
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    you can convert it into an Array.

    View full-size slide

  95. Slide № 95
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 >

    View full-size slide

  96. Slide № 96
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > Now, I’m only interested in pairs of consecutive digits where
    the left matches the right.

    View full-size slide

  97. Slide № 97
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a

    View full-size slide

  98. Slide № 98
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .

    View full-size slide

  99. Slide № 99
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter {
    So I’m gonna filter this Enumerator…

    View full-size slide

  100. Slide № 100
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a,
    …taking 2 consecutive digits

    View full-size slide

  101. Slide № 101
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b|
    …taking 2 consecutive digits, a and b…

    View full-size slide

  102. Slide № 102
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    …and looking for pairs where a equals b.

    View full-size slide

  103. Slide № 103
    2.4.1 :005 > (input + input[0]).chars.map(&:to_i)
    => [1, 2, 2, 3, 3, 3, 4, 5, 5, 1, 1]
    2.4.1 :006 > (input + input[0]).chars.map(&:to_i).each_cons(2)
    => #
    2.4.1 :007 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .to_a
    => [[1, 2], [2, 2], [2, 3], [3, 3], [3, 3], [3, 4], [4, 5],
    [5, 5], [5, 1], [1, 1]]
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8

    View full-size slide

  104. Slide № 104
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > As I’m a JavaScript user, I always forget that
    in Ruby, `filter` is called `select`.

    View full-size slide

  105. Slide № 105
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }

    View full-size slide

  106. Slide № 106
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    . { |a, b| a == b }

    View full-size slide

  107. Slide № 107
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }

    View full-size slide

  108. Slide № 108
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }
    => [[2, 2], [3, 3], [3, 3], [5, 5], [1, 1]]
    2.4.1 :010 > Now I have the pairs of consecutive digits that match.

    View full-size slide

  109. Slide № 109
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }
    => [[2, 2], [3, 3], [3, 3], [5, 5], [1, 1]]
    2.4.1 :010 >

    View full-size slide

  110. Slide № 110
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }
    => [[2, 2], [3, 3], [3, 3], [5, 5], [1, 1]]
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }
    I’m only interested in the digits, so I’m going to…

    View full-size slide

  111. Slide № 111
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }
    => [[2, 2], [3, 3], [3, 3], [5, 5], [1, 1]]
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    …take the first item out of each pair.

    View full-size slide

  112. Slide № 112
    2.4.1 :008 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .filter { |a, b| a == b }
    NoMethodError: undefined method `filter' for #2, 3, 3, 3, 4, 5, 5, 1, 1]:each_cons(2)>
    from (irb):8
    from /Users/dtinth/.rvm/rubies/ruby-2.4.1/bin/irb:11:in
    `'
    2.4.1 :009 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }
    => [[2, 2], [3, 3], [3, 3], [5, 5], [1, 1]]
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]

    View full-size slide

  113. Slide № 113
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]
    2.4.1 :011 >

    View full-size slide

  114. Slide № 114
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]
    2.4.1 :011 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    Finally, the last thing I have to do is…

    View full-size slide

  115. Slide № 115
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]
    2.4.1 :011 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first).inject(&:+)
    …to sum em up. I’m gonna inject the + sign between each element.
    Note from audience: Enumerables also have `sum` method since Ruby 2.4.
    (Thanks, Michael!)

    View full-size slide

  116. Slide № 116
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]
    2.4.1 :011 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first).inject(&:+)
    => 14
    …and there’s the answer.

    View full-size slide

  117. Slide № 117
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]
    2.4.1 :011 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first).inject(&:+)
    => 14
    One-liner that solves the problem

    View full-size slide

  118. Slide № 118
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]
    2.4.1 :011 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first).inject(&:+)
    => 14
    One-liner that solves the problem
    Very fast feedback loop
    Get immediate feedback whenever something goes wrong.

    View full-size slide

  119. Slide № 119
    2.4.1 :010 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first)
    => [2, 3, 3, 5, 1]
    2.4.1 :011 > (input + input[0]).chars.map(&:to_i).each_cons(2) \
    .select { |a, b| a == b }.map(&:first).inject(&:+)
    => 14
    One-liner that solves the problem
    Very fast feedback loop
    Iterative and incremental
    Instead of having to solve the whole problem at once, I start with the problem,
    and gradually transform it until a solution is reached.

    View full-size slide

  120. Slide № 120
    Ruby allows for very fast feedback loop

    View full-size slide

  121. Slide № 121
    If you use a modern text editor…

    View full-size slide

  122. Slide № 122
    …you can edit your code inside the text editor…

    View full-size slide

  123. Slide № 123
    …and run a command that will send the current line into the terminal…

    View full-size slide

  124. Slide № 124
    …this is how I like to solve problems in Ruby.

    View full-size slide

  125. Slide № 125
    (input + input[0]).chars.map(&:to_i).each_cons(2)
    .select { |a, b| a == b }.map(&:first).inject(&:+)
    Let’s come back and take a look at this code again…

    View full-size slide

  126. Slide № 126
    (input + input[0]).chars.map(&:to_i).each_cons(2)
    .select { |a, b| a == b }.map(&:first).inject(&:+)
    String#[]
    String#+
    String#chars
    Enumerable#map
    Enumerable#each_cons
    Enumerable#select
    Enumerable#inject
    I used a lot of methods to solve this problem.
    This brings me to the second point:

    View full-size slide

  127. Slide № 127
    Ruby has a very rich core library
    Compared to many other languages, I can do a lot using only built-in methods,
    without having to `npm install` anything or import any library. A lot is built-in.

    View full-size slide

  128. Slide № 128
    My favorite tricks
    Skipping map, select, inject, compact, sort_by,
    and most basic FP tricks found in most languages.
    Next, I will talk about my favorite tricks.
    I will skip the most basic functional programming techniques like
    `map`, `reduce`, `filter`, and will instead focus on
    the patterns that I use very often.

    View full-size slide

  129. Slide № 129
    Generate array contents programmatically.
    3
    123
    456
    789
    stdin
    When solving programming challenges,
    I often have to read an array of numbers from an input file.
    The first line is the number of elements,
    and subsequent lines contain each element’s value.

    View full-size slide

  130. Slide № 130
    n = gets.to_i
    a = Array.new(n)
    (0...n).each do |i|
    a[i] = gets.to_i
    end
    => [123, 456, 789]
    Generate array contents programmatically.
    3
    123
    456
    789
    stdin
    Most people I know would write a loop like this.
    Again, in their favorite language.

    View full-size slide

  131. Slide № 131
    Array.new(gets.to_i) { gets.to_i }
    => [123, 456, 789]
    Generate array contents programmatically.
    3
    123
    456
    789
    stdin
    But you can also pass a block to Array initializer, and it’s going to run the block
    for each element. So, that loop has been reduced to a single line.

    View full-size slide

  132. Slide № 132
    2.4.1 :001 > tally = Hash.new(0)
    => {}
    2.4.1 :002 > tally['b']
    => 0 # normal hash: nil
    2.4.1 :003 > tally['a'] += 1
    => 1 # normal hash: undefined method `+` for nil
    2.4.1 :004 > tally
    => {"a"=>1}
    Hash can have default value
    Hashes in Ruby can have a default value, so it’s really easy to do tallying
    without having to worry about `nil` when reading nonexistent keys from the hash.

    View full-size slide

  133. Slide № 133
    2.4.1 :001 > visited = Hash.new(false)
    => {}
    2.4.1 :002 > visited[[0, 0]] = true
    => true
    Array can be hash keys
    In Ruby you can also use arrays as hash keys. This is very useful for representing
    sparse matrices. Also useful when walking on a 2D map, keeping track of which
    (x, y) coordinates have been visited.

    View full-size slide

  134. Slide № 134
    Useful array methods
    2.4.1 :001 > [1, 2, 3, 4, 5].rotate(2)
    => [3, 4, 5, 1, 2]
    2.4.1 :002 > [1, 2, 3, 4, 5].rotate(-2)
    => [4, 5, 1, 2, 3]
    Ruby has a very rich array methods, such as rotate…

    View full-size slide

  135. Slide № 135
    Useful array methods
    2.4.1 :001 > [1, 2, 3, 4].permutation.to_a
    => [[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4],
    [1, 3, 4, 2], [1, 4, 2, 3], [1, 4, 3, 2],
    [2, 1, 3, 4], [2, 1, 4, 3], [2, 3, 1, 4],
    [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1],
    [3, 1, 2, 4], [3, 1, 4, 2], [3, 2, 1, 4],
    [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1],
    [4, 1, 2, 3], [4, 1, 3, 2], [4, 2, 1, 3],
    [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]
    …or permutation, which gives you all possible permutations of array elements.

    View full-size slide

  136. Slide № 136
    Useful array methods
    2.4.1 :001 > [1, 2, 3, 4].permutation(2).to_a
    => [ [1, 2], [1, 3], [1, 4],
    [2, 1], [2, 3], [2, 4],
    [3, 1], [3, 2], [3, 4],
    [4, 1], [4, 2], [4, 3]]
    You can specify the number of elements to permute…

    View full-size slide

  137. Slide № 137
    Useful array methods
    2.4.1 :001 > [1, 2, 3, 4].repeated_permutation(2).to_a
    => [[1, 1], [1, 2], [1, 3], [1, 4],
    [2, 1], [2, 2], [2, 3], [2, 4],
    [3, 1], [3, 2], [3, 3], [3, 4],
    [4, 1], [4, 2], [4, 3], [4, 4]]
    …and whether you want repeated elements or not.

    View full-size slide

  138. Slide № 138
    Useful array methods
    2.4.1 :001 > [1, 2, 3, 4].combination(2).to_a
    => [ [1, 2], [1, 3], [1, 4],
    [2, 3], [2, 4],
    [3, 4]]
    `combination` gives you all possible ways to choose N elements from an array…

    View full-size slide

  139. Slide № 139
    Useful array methods
    2.4.1 :001 > [1, 2, 3, 4].repeated_combination(2).to_a
    => [[1, 1], [1, 2], [1, 3], [1, 4],
    [2, 2], [2, 3], [2, 4],
    [3, 3], [3, 4],
    [4, 4]]
    …and again, you can choose whether you want repeated elements.

    View full-size slide

  140. Slide № 140
    Useful enumerable methods
    2.4.1 :001 > [1, 2, 3, 4, 5, 6, 7].each_cons(3).to_a
    => [[1, 2, 3],
    [2, 3, 4],
    [3, 4, 5],
    [4, 5, 6],
    [5, 6, 7]]
    For things that are Enumerable, I find myself using `each_cons` a lot.

    View full-size slide

  141. Slide № 141
    Useful enumerable methods
    2.4.1 :001 > [1, 2, 3, 4, 5, 6, 7].each_slice(3).to_a
    => [[1, 2, 3], [4, 5, 6], [7]]
    `each_slice` too, useful when processing things in chunks.

    View full-size slide

  142. Slide № 142
    Rational numbers
    2.4.1 :001 > 4/6r
    => (2/3)
    Rational numbers in Ruby are super useful when I have to deal with fractions
    but don’t want to deal with floating point rounding errors.

    View full-size slide

  143. Slide № 143
    Hash <=> Enumerable <=> Array
    File.readlines('/usr/share/dict/words')
    .group_by { |w| w.chars.first.downcase }
    .map { |char, words| [char, words.count] }
    .to_h
    => {"a"=>17096, "b"=>11070, "c"=>19901, "d"=>10896,
    "e"=>8736, "f"=>6860, "g"=>6861, "h"=>9027, "i"=>8799,
    "j"=>1642, "k"=>2281, "l"=>6284, "m"=>12616,
    "n"=>6780, "o"=>7849, "p"=>24461, "q"=>1152,
    "r"=>9671, "s"=>25162, "t"=>12966, "u"=>16387,
    "v"=>3440, "w"=>3944, "x"=>385, "y"=>671, "z"=>949}
    Enumerable#to_a
    Enumerable#to_h
    One thing I especially like about Ruby is that both Arrays and Hashes are Enumerable.
    And you can convert an Enumerable into both an Array and a Hash.

    View full-size slide

  144. Slide № 144
    Lambdas
    2.4.1 :001 > -> x, y { x*x + 2*x*y + y*y }[15, 2]
    => 289
    And finally, lambda expressions are really useful
    when I want to temporarily refer to a value multiple times.

    View full-size slide

  145. Slide № 145
    Day-to-day programming
    Apart from competitive programming, I find myself using Ruby a lot in…
    …again, I mostly write JavaScript.

    View full-size slide

  146. Slide № 146
    JavaScript refactoring

    View full-size slide

  147. Slide № 147
    export const TYPE_TASK_CREATE = 1
    export const TYPE_TASK_DELETE = 2
    export const TYPE_PROJECT_CREATE_OR_UPDATE = 3
    export const TYPE_TASK_UPDATE = 5
    export const TYPE_PROJECT_UPDATE = 6
    export const TYPE_PROJECT_DELETE = 7
    export const TYPE_DIRECT_MESSAGE = 11
    export const TYPE_TASK_COMMENT = 12
    export const TYPE_UPCOMING = 13
    export const TYPE_OVERDUE = 14
    (This comes from actual production app)
    We are converting part of our app to TypeScript.

    View full-size slide

  148. Slide № 148
    export const TYPE_TASK_CREATE = 1
    export const TYPE_TASK_DELETE = 2
    export const TYPE_PROJECT_CREATE_OR_UPDATE = 3
    export const TYPE_TASK_UPDATE = 5
    export const TYPE_PROJECT_UPDATE = 6
    export const TYPE_PROJECT_DELETE = 7
    export const TYPE_DIRECT_MESSAGE = 11
    export const TYPE_TASK_COMMENT = 12
    export const TYPE_UPCOMING = 13
    export const TYPE_OVERDUE = 14
    Convert to TypeScript enum

    View full-size slide

  149. Slide № 149
    export const TYPE_TASK_CREATE = 1
    export const TYPE_TASK_DELETE = 2
    export const TYPE_PROJECT_CREATE_OR_UPDATE = 3
    export const TYPE_TASK_UPDATE = 5
    export const TYPE_PROJECT_UPDATE = 6
    export const TYPE_PROJECT_DELETE = 7
    export const TYPE_DIRECT_MESSAGE = 11
    export const TYPE_TASK_COMMENT = 12
    export const TYPE_UPCOMING = 13
    export const TYPE_OVERDUE = 14
    export enum NotificationType {
    TaskCreate = 1,
    TaskDelete = 2,
    ProjectCreateOrUpdate = 3,
    TaskUpdate = 5,
    ProjectUpdate = 6,
    ProjectDelete = 7,
    DirectMessage = 11,
    TaskComment = 12,
    Upcoming = 13,
    Overdue = 14,
    Convert to TypeScript enum

    View full-size slide

  150. Slide № 150
    export const TYPE_TASK_CREATE = 1
    export const TYPE_TASK_DELETE = 2
    export const TYPE_PROJECT_CREATE_OR_UPDATE = 3
    export const TYPE_TASK_UPDATE = 5
    export const TYPE_PROJECT_UPDATE = 6
    export const TYPE_PROJECT_DELETE = 7
    export const TYPE_DIRECT_MESSAGE = 11
    export const TYPE_TASK_COMMENT = 12
    export const TYPE_UPCOMING = 13
    export const TYPE_OVERDUE = 14
    export enum NotificationType {
    TaskCreate = 1,
    TaskDelete = 2,
    ProjectCreateOrUpdate = 3,
    TaskUpdate = 5,
    ProjectUpdate = 6,
    ProjectDelete = 7,
    DirectMessage = 11,
    TaskComment = 12,
    Upcoming = 13,
    Overdue = 14,
    Convert to TypeScript enum
    TYPE_TASK_CREATE => TaskCreate
    Following TypeScript’s naming convention,
    some naming scheme has to be changed.

    View full-size slide

  151. Slide № 151
    export const TYPE_TASK_CREATE = 1
    export const TYPE_TASK_DELETE = 2
    export const TYPE_PROJECT_CREATE_OR_UPDATE = 3
    export const TYPE_TASK_UPDATE = 5
    export const TYPE_PROJECT_UPDATE = 6
    export const TYPE_PROJECT_DELETE = 7
    export const TYPE_DIRECT_MESSAGE = 11
    export const TYPE_TASK_COMMENT = 12
    export const TYPE_UPCOMING = 13
    export const TYPE_OVERDUE = 14
    export enum NotificationType {
    TaskCreate = 1,
    TaskDelete = 2,
    ProjectCreateOrUpdate = 3,
    TaskUpdate = 5,
    ProjectUpdate = 6,
    ProjectDelete = 7,
    DirectMessage = 11,
    TaskComment = 12,
    Upcoming = 13,
    Overdue = 14,

    View full-size slide

  152. Slide № 152
    export const TYPE_TASK_CREATE = 1
    export const TYPE_TASK_DELETE = 2
    export const TYPE_PROJECT_CREATE_OR_UPDATE = 3
    export const TYPE_TASK_UPDATE = 5
    export const TYPE_PROJECT_UPDATE = 6
    export const TYPE_PROJECT_DELETE = 7
    export const TYPE_DIRECT_MESSAGE = 11
    export const TYPE_TASK_COMMENT = 12
    export const TYPE_UPCOMING = 13
    export const TYPE_OVERDUE = 14
    export enum NotificationType {
    TaskCreate = 1,
    TaskDelete = 2,
    ProjectCreateOrUpdate = 3,
    TaskUpdate = 5,
    ProjectUpdate = 6,
    ProjectDelete = 7,
    DirectMessage = 11,
    TaskComment = 12,
    Upcoming = 13,
    Overdue = 14,
    Deduplicate

    View full-size slide

  153. Slide № 153
    export const TYPE_TASK_CREATE = NotificationType.TaskCreate
    export const TYPE_TASK_DELETE = NotificationType.TaskDelete
    export const TYPE_PROJECT_CREATE_OR_UPDATE = NotificationType.ProjectCreateOrUpdate
    export const TYPE_TASK_UPDATE = NotificationType.TaskUpdate
    export const TYPE_PROJECT_UPDATE = NotificationType.ProjectUpdate
    export const TYPE_PROJECT_DELETE = NotificationType.ProjectDelete
    export const TYPE_DIRECT_MESSAGE = NotificationType.DirectMessage
    export const TYPE_TASK_COMMENT = NotificationType.TaskComment
    export const TYPE_UPCOMING = NotificationType.Upcoming
    export const TYPE_OVERDUE = NotificationType.Overdue
    export enum NotificationType {
    TaskCreate = 1,
    TaskDelete = 2,
    ProjectCreateOrUpdate = 3,
    TaskUpdate = 5,
    ProjectUpdate = 6,
    ProjectDelete = 7,
    DirectMessage = 11,
    TaskComment = 12,
    Upcoming = 13,
    Overdue = 14,
    (The old API is still available
    to minimize breaking change,
    though deprecated)

    View full-size slide

  154. Slide № 154
    Live demo
    puts code.scan(/(TYPE_\w+) = (\d+)/)

    .map { |name, value| "#{name.split('_').map(&:capitalize).drop(1).join} = {value}" }
    .join(",\n")
    Part 1: Generate the enum entries.
    puts code.gsub(/(TYPE_\w+) = (\d+)/) {
    "#{$1} = NotificationType.#{$1.split('_').map(&:capitalize).drop(1).join}"
    }
    Part 2: Use the enum in legacy API.

    View full-size slide

  155. Slide № 155
    Conclusions

    View full-size slide

  156. Slide № 156
    Tight feedback loop
    Ruby has…
    via irb

    View full-size slide

  157. Slide № 157
    Tight feedback loop
    +
    Simple, expressive, not too strict syntax
    Ruby has…

    View full-size slide

  158. Slide № 158
    Tight feedback loop
    +
    Simple, expressive, not too strict syntax
    +
    Great and intuitive built-in library
    Ruby has…

    View full-size slide

  159. Slide № 159
    Tight feedback loop
    +
    Simple, expressive, not too strict syntax
    +
    Great and intuitive built-in library
    ↓ ↓ ↓
    Great language for solving problems!
    Ruby has…

    View full-size slide

  160. Slide № 160
    Effective problem solving in Ruby
    API docs for: String, Numeric, Rational,
    Range, Array, Hash, Enumerable, Proc
    stdlib: matrix, prime, pp
    For me to solve problems effectively in Ruby,

    I find myself reading through the API docs of common classes every now and then…
    …reading through each methods in a class, from top to bottom…
    I recommend that you do this too — This may sound like an obvious thing to do,
    but I don’t see developers doing this enough. Me included — I was not aware
    of the recently-added `sum` method…

    View full-size slide

  161. Slide № 161
    Thank you!!

    View full-size slide