Date post: | 26-Jun-2015 |
Category: |
Technology |
Upload: | callmewuest |
View: | 198 times |
Download: | 1 times |
FUNCTIONAL RUBY
FIRST-CLASS FUNCTIONS
You already use these# Print each number from 1-30 in hexadecimal(1..30).each do |number| puts number.to_s(16)end
FIRST-CLASS FUNCTIONS
Changing the example slightly:# Create a lambda which prints a number in hexadecimalputs_in_hex = lambda do |number| puts number.to_s(16)end
(1..10).each { |i| puts_in_hex.call(i) }
FIRST-CLASS FUNCTIONS
This can be further simplified:puts_in_hex = lambda do |number| puts number.to_s(16)end
(1..10).each(&puts_in_hex)
ASIDE: THE AMPERSAND
The ampersand indicates that we're working with a blockdef use_block(&block) block.call(2)end
use_block do |number| number * 2end# => 4
ASIDE: THE AMPERSAND
It also causes a viariable to be interpreted as a block.def use_block(&block) block.call(2)end
multiply_two = lambda do |number| number * 2end
use_block(&multiply_two)# => 4
ASIDE: SYMBOL#TO_PROC
Creates a proc which will call a method on an objectcall_to_s = :to_s.to_proc
# Looks something like this...proc do |obj, *args| obj.send(:to_s, *args)end
# Ends up being 10.send(:to_s) or 10.to_scall_to_s.call(10)# => "10"
# Ends up being 10.send(:to_s, 16) or 10.to_s(16)call_to_s.call(10, 16)# => "a"
ASIDE: SYMBOL#TO_PROC
In a method call, &:sym is a shortcut for :sym.to_procdef apply_block_to_array_and_print(&block) yield ['h', 'e', 'll', 'o', ' ', 'fun', 'ctions', '!']end
apply_block_to_array_and_print(&:join)# => "hello functions!"
FILTERProblem: A list needs to be filtered
FILTERSolution: delete everything else
# Find all of the adverbs in a word listword_list.each do |item| word_list.delete(item) unless /ly$/.match(item)end
FILTERBetter solution: build a new list!
# Find all of the adverbs in a word listadverbs = []word_list.each do |item| adverbs << item if /ly$/.match(item)end
FILTERBetter yet: use Enumerable#select
# Find all of the adverbs and non-adverbs in a word listadverbs = word_list.select { |item| /ly$/.match(item) }not_adverbs = word_list.reject { |item| /ly$/.match(item) }
MAPProblem: A list needs to have elements modified
MAPSolution: Overwrite the original list
# Square all of our numbers(1...numbers.length).each do |index| numbers[index] **= 2end
MAPBetter Solution: generate a new list
numbers = [1,2,3,4,5]squares = []
# Square all of the numbersnumbers.each do |number| squares << number ** 2end
MAPBetter yet: use Enumerable#map
numbers = [1, 2, 3, 4, 5]
# Square all of our numberssquares = numbers.map { |number| number ** 2 }
# Another way we could do itsquares = numbers.each_with_object(2).map(&:**)
REDUCEProblem: A list needs to be transformed
REDUCESolution: Iterate through the list
numbers = [1, 2, 3, 4, 5]product = 1
# Alter the product iterativelynumbers.each do |number| product *= numberend
REDUCEBetter solution: Use Enumerable#reduce
numbers = [1, 2, 3, 4, 5]
# Calculate the product of the list membersproduct = numbers.reduce { |acc,item| acc * item }
# Shorter way to do the sameproduct = numbers.reduce(&:*)
ZIPProblem: Two lists need to be intertwined
ZIPSolution: Overwrite one of the lists
a = [1, 2, 3]b = [4, 5, 6]
# Intertwine list a with list ba.each_with_index do |number, index| a[index] = [number, b[index]]end
ZIPBetter Solution: use Enumerable#zip
a = [1, 2, 3]b = [4, 5, 6]
# Intertwine list a with list bc = a.zip(b)# => [[1, 4], [2, 5], [3, 6]]
WARNINGReligion ahead!
STATERuby is good at state.
Mutable
Implicit
Hidden
DANGEROUS STATE
Consider:given_names = %w(Alice Bob Eve Mallory)
short_names = given_names.select { |name| name.length < 5 }short_names.each { |name| puts name.upcase! }
given_names[1] # => ???
DANGEROUS STATE
Consider:given_names = %w(Alice Bob Eve Mallory)
short_names = given_names.select { |name| name.length < 5 }
short_names.each { |name| puts name.upcase! }
given_names[1] # => "BOB"
DUP TO THE RESCUE?Maybe Object#dup will help?:
given_names = %w(Alice Bob Eve Mallory)safe_names = given_names.dup
short_names = safe_names.select { |name| name.length < 5 }short_names.each { |name| puts name.upcase! }
given_names[1] # => ???
DUP TO THE RESCUE?Maybe Object#dup will help?:
given_names = %w(Alice Bob Eve Mallory)
safe_names = given_names.dup
short_names = safe_names.select { |name| name.length < 5 }
short_names.each { |name| puts name.upcase! }
given_names[1] # => "BOB"
WELP.State is difficult to manage and trackParticularly as systems grow in complexityThings get more difficult with real threads (Rubinius, JRuby) Avoiding mutable state in most cases avoids this problem.
BATTLING STATEAvoiding state fits well with good style:
Keep methods short and responsible for one thingWrite methods with idempotence in mindWhen mutations seem necessary, use more functions
RULES: MADE TO BE BROKEN
Ruby exposes state to the programmer in a dangerous way
Once concurrency comes into play, scary dragons emerge
Avoiding mutable state helps, but can be expensive
PAIN POINTS
Sometimes avoiding state doesn't make sense:Code runs much heavier than it couldCode runs much slower than it otherwise might (GC runs)
PAIN MANAGEMENTWe can keep things from getting out of hand!
Keep code which has side-effects to a minimumIsolate code which produces side-effectsDon't make it easy to mutate state accidentally
QUESTIONS? COMMENTS?