Thinking Functionally In Ruby

2,997 views

Published on

Published in: Technology
0 Comments
9 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,997
On SlideShare
0
From Embeds
0
Number of Embeds
645
Actions
Shares
0
Downloads
70
Comments
0
Likes
9
Embeds 0
No embeds

No notes for slide

Thinking Functionally In Ruby

  1. 1. THINKING FUNCTIONALLY IN RUBY. @tomstuart
  2. 2. 1: Functional programming is a pretty neat idea.
  3. 3. 2: Enumerable contains some useful methods.
  4. 4. I hope these things are connected.
  5. 5. 1. (Functional programming is a pretty neat idea)
  6. 6. What is functional programming?
  7. 7. A programming style.
  8. 8. Two broad families of languages:
  9. 9. • Scheme • Common Lisp • Dylan • Clojure 1. Lisp-like • Dynamically typed • Homoiconic (code is data) • Lists are fundamental
  10. 10. • Standard ML • Haskell • OCaml • Scala (sort of) 2. ML-like • Statically typed (+ reconstruction) • Code is not data • ADTs and pattern matching
  11. 11. Functions as values
  12. 12. What is a function?
  13. 13. 1 2 7 6 2 3 3 6 1 7 5 4 8 4
  14. 14. Functions as values
  15. 15. “First-class functions” “closures”, “lambdas”, “anonymous functions” Implies: higher-order functions
  16. 16. No side effects
  17. 17. Values are immutable All functions do is return their result No state No I/O
  18. 18. (In reality, different languages accomplish this to varying degrees.)
  19. 19. Implies: recursion Implies: persistent data structues
  20. 20. So what?
  21. 21. Unfortunately...
  22. 22. Declarative T! W HA programming is counterintuitive when youʼre accustomed to imperative HOW ! programming
  23. 23. Copying is expensive
  24. 24. But!
  25. 25. Referential transparency “an expression can be replaced with its value” • Good for efficiency • caching/memoisation/inlining • ultimately more efficient • Good for programmer understanding • state is incredibly hard to deal with • ultimately more intuitive • Good for code reuse and testing
  26. 26. Highly expressive Strongly compositional Deeply satisfying
  27. 27. Future-proof • Mooreʼs Law is running out of steam • similar transistor density, more cores • The futureʼs concurrent • Concurrent access to mutable state is hard... • but not if you donʼt have mutable state! • Parallelising an algorithm is hard... • but itʼs easier if your code isnʼt overspecified
  28. 28. OK seriously: so what?
  29. 29. Ruby can do some of this. Not really a functional language, but we can pretend and get some of the benefits.
  30. 30. Use function values blocks, procs lambda { |x| x + 1 }
  31. 31. Consider treating your values as immutable State is rarely worth it. It will kill you in the end.
  32. 32. http://clojure.org/state
  33. 33. Use the functional-flavoured parts of the standard library
  34. 34. Think functionally. Be declarative. What, not how.
  35. 35. 2. (Enumerable contains some useful methods)
  36. 36. Enumerable#zip
  37. 37. [1, 2, 3, 4]. zip([5, 6, 7, 8])
  38. 38. 1 2 3 4 5 6 7 8
  39. 39. 1 5 2 6 3 7 4 8
  40. 40. [ ] [1 , 5],[2 , 6],[3 , 7],[4 , 8]
  41. 41. [1, 2, 3, 4]. zip([5, 6, 7, 8], [9, 10, 11, 12])
  42. 42. Enumerable#select (a.k.a. #find_all)
  43. 43. { |x| x.odd? }
  44. 44. 1 ?
  45. 45. 1 ! ? 1
  46. 46. 1 2 ! ? 1 ?
  47. 47. 1 2 ! ? 1 " ? 2
  48. 48. [1, 2, 3, 4]. select { |x| x.odd? }
  49. 49. 1 2 3 4 ? ? ? ?
  50. 50. 1 2 3 4 ! ? 1 " ? 2 ! ? 3 " ? 4
  51. 51. 1 3 ! ? 1 ! ? 3 1 3
  52. 52. [ 1, 3 ]
  53. 53. Enumerable#partition
  54. 54. [1, 2, 3, 4]. partition { |x| x.odd? }
  55. 55. 1 2 3 4 ? ? ? ?
  56. 56. 1 2 3 4 ! ? 1 " ? 2 ! ? 3 " ? 4
  57. 57. 1 3 2 4 ! ? ! ? " ? " ?
  58. 58. 1 3 2 4 ! ? ! ? " ? " ? 1 3 2 4
  59. 59. [[ 1 , 3 ] , [ 2 , 4 ]]
  60. 60. Enumerable#map (a.k.a. #collect)
  61. 61. { |x| x * 3 }
  62. 62. 2 ×3 6
  63. 63. 2 ×3 2 6
  64. 64. [1, 2, 3, 4]. map { |x| x * 3 }
  65. 65. 1 2 3 4 ×3 3 ×3 6 ×3 9 ×3 12
  66. 66. 1 2 3 4 ×3 1 ×3 2 ×3 3 ×3 4 3 6 9 12
  67. 67. [ , , , ] 3 6 9 12
  68. 68. Enumerable#inject (a.k.a. #reduce)
  69. 69. { |x, y| x + y }
  70. 70. 3 5 8 +
  71. 71. 3 5 3+5 8
  72. 72. [1, 2, 3, 4]. inject(0) { |x,y| x+y }
  73. 73. 0 1 1 + 2 3 + 3 6 + 4 + 10
  74. 74. 0 1 0+1 1 2 1+2 3 3 3+3 6 4 6 + 4 10
  75. 75. module Enumerable def inject(initial) result = initial for element in self result = yield(result, element) end result end end
  76. 76. a.k.a. “left fold” (foldl, fold_left)
  77. 77. 0 1 2 3 4
  78. 78. 10 (((0 + 1) + 2) + 3) + 4
  79. 79. ...versus “right fold” (foldr, fold_right)
  80. 80. 1 2 3 4 0
  81. 81. 10 1 + (2 + (3 + (4 + 0)))
  82. 82. The initial argument is optional...
  83. 83. [1, 2, 3, 4]. inject { |x,y| x+y }
  84. 84. [2, 3, 4].4]. [1, 2, 3, inject(1)|x,y| x+yx+y } inject { { |x,y| }
  85. 85. ...but only if the output is the same type as the input...
  86. 86. >> ['El', 'rug']. inject(0) { |l,s| l + s.length } => 5 >> ['El', 'rug']. inject { |l,s| l + s.length } TypeError: can't convert Fixnum into String from (irb):1:in `+' from (irb):1 from (irb):1:in `inject' from (irb):1:in `each' from (irb):1:in `inject' from (irb):1
  87. 87. ...and itʼs meaningful to get nil when the collection is empty
  88. 88. >> [].inject { |x,y| x+y } => nil >> [].inject(0) { |x,y| x+y } => 0 >> [].inject(1) { |x,y| x*y } => 1
  89. 89. P O SE ! C O M
  90. 90. [1, 2, 3, 4]. map { |x| x * 3 }. inject(0) { |x| x+y }
  91. 91. 1 2 3 4 ×3 3 ×3 6 ×3 9 ×3 12 0 3 + 9 + + 18 + 30
  92. 92. 1 2 3 4 ×3 1 ×3 2 ×3 3 ×3 4 0 3 0+3 3 6 3+6 9 9 9 + 9 18 12 18+12 30
  93. 93. I am so excited. What now?
  94. 94. Review your Ruby code. Func it up.
  95. 95. result = '' for name in names unless result.empty? result << ', ' end result << name end result
  96. 96. ! result = '' for name in names unless result.empty? result << ', ' end result << name end result names.join(', ')
  97. 97. def count_mines_near(x, y) count = 0 for i in x-1..x+1 for j in y-1..y+1 count += 1 if mine_at?(i, j) end end count end
  98. 98. ! def count_mines_near(x, y) count = 0 for i in x-1..x+1 for j in y-1..y+1 count += 1 if mine_at?(i, j) end end count end def count_mines_near(x, y) ((x-1..x+1).entries * 3).sort. zip((y-1..y+1).entries * 3). select { |x, y| mine_at?(x, y) }. length end
  99. 99. [1, 2, 3, 1, 2, 3, 1, 2, 3].sort = [1, 1, 1, 2, 2, 2, 3, 3, 3] def count_mines_near(x, y) # = (2, 8) ((x-1..x+1).entries * 3).sort. zip((y-1..y+1).entries * 3). select { |x, y| mine_at?(x, y) }. length end
  100. 100. [1, 2, 3, 1, 2, 3, 1, 2, 3].sort = [1, 1, 1, 2, 2, 2, 3, 3, 3].zip( [7, 8, 9, 7, 8, 9, 7, 8, 9]) = [[1, 7], [1, 8], [1, 9], [2, 7], [2, 8], [2, 9], [3, 7], [3, 8], [3, 9]] def count_mines_near(x, y) # = (2, 8) ((x-1..x+1).entries * 3).sort. zip((y-1..y+1).entries * 3). select { |x, y| mine_at?(x, y) }. length end
  101. 101. [1, 2, 3, 1, 2, 3, 1, 2, 3].sort = [1, 1, 1, 2, 2, 2, 3, 3, 3].zip( [7, 8, 9, 7, 8, 9, 7, 8, 9]) = [[1, 7], [1, 8], [1, 9], [2, 7], [2, 8], [2, 9], [3, 7], [3, 8], [3, 9]].select {…} = [[1, 8], [3, 7]].length = 2 def count_mines_near(x, y) # = (2, 8) ((x-1..x+1).entries * 3).sort. zip((y-1..y+1).entries * 3). select { |x, y| mine_at?(x, y) }. length end
  102. 102. [ [1, 7], [1, 8], …, [1, 1007], [2, 7], [2, 8], …, [2, 1007], …, …, …, [1000, 7], [1000, 8], …, [1000, 1007], [1001, 7], [1000, 8], …, [1001, 1007]] def count_mines_near(x, y) ((x-500..x+500).entries * 1001).sort. zip((y-500..y+500).entries * 1001). select { |x, y| mine_at?(x, y) }. length end
  103. 103. [ [1, 7], [1, 8], …, [1, 1007], [2, 7], [2, 8], …, [2, 1007], …, …, …, [1000, 7], [1000, 8], …, [1000, 1007], [1001, 7], [1000, 8], …, [1001, 1007]] 173 96 121 78 237 705
  104. 104. def numbers_near(n, radius) ((n - radius)..(n + radius)).entries end def squares_near(x, y, radius) diameter = (radius * 2) + 1 (numbers_near(x, radius) * diameter). sort. zip(numbers_near(y, radius) * diameter) end def count_mines_near(x, y, radius = 1) squares_near(x, y, radius). select { |x, y| mine_at?(x, y) }. length end
  105. 105. Learn a functional language • OCaml • Scheme (Google “SICP”) • Clojure • Scala • Haskell? Erlang?
  106. 106. Thanks! @tomstuart / tom@experthuman.com
  107. 107. http://www.flickr.com/photos/somemixedstuff/2403249501/ http://en.wikipedia.org/wiki/File:Modified-pc-case.png

×