Reputation: 579
I am working my way through this article on functional programming (https://codewords.recurse.com/issues/one/an-introduction-to-functional-programming) and trying to do the exercises in Ruby.
One exercise defines two functions, zero and one. Zero takes a string parameter and returns string index 1 - end if the first character is 0, One does the same thing but only if the first character is one.
Here are the ruby implementations:
def zero(s)
if s[0] == "0"
return s[1..(s.length)]
end
end
def one(s)
if s[0] == "1"
return s[1..(s.length)]
end
end
The problem asks you to write a method called rule_sequence that, given a string and an array of functions, returns the result such that the functions are invoked one at a time -- the first function is invoked in the whole string, the second function invoked on the return value of that string, etc. If, at any point, the one of the functions returns nil, return nil.
The Python implementation is:
def rule_sequence(s, rules):
if s == None or not rules:
return s
else:
return rule_sequence(rules[0](s), rules[1:])
However, since it doesn't seem like Ruby has support for higher order functions, the most elegant solution I could come up with was the following:
def rule_sequence(string, rules)
if rules.length == 0 or string.nil?
return string
else
return rule_sequence(rules[0].call(string), rules[1..rules.length])
end
end
puts rule_sequence('0101', [lambda { |s| zero(s) }, lambda { |s| one(s) }, lambda { |s| zero(s) } ])
Can anyone come up with something more terse than passing or calling lambdas?
Upvotes: 5
Views: 2408
Reputation: 526
I'll take this exercise as an opportunity to show how Ruby supports higher order functions.
Let's go one step back and rewrite the zero
- one
functions.
You can notice they have quite a lot in common.
Let's try to exploit that by writing a lambda that can generate both
tail_on_prefix = lambda {|prefix|
lambda {|str| str[1..-1] if str[0] == prefix}
}
We can now easily define zero
and one
zero = tail_on_prefix.("0")
one = tail_on_prefix.("1")
To rule_sequence
now!
rule_sequence = lambda {|str, rules|
if (str.nil? or rules.empty?)
str
else
rule_sequence.(rules[0].(str), rules[1..-1])
end
}
now calling rule_sequence
looks a bit better, doesn't it
rule_sequence.("100101", [one, zero, zero]) # => "101"
Upvotes: 7
Reputation: 2057
You can do this
puts rule_sequence('0101', [method(:zero), method(:one), method(:zero)])
Just BTW, s[1..(s.length)]
can be written as s[1..-1]
, and rules[0].call(string)
can be written as rules[0].(string)
.
Upvotes: 3