Ruby use the functions from “functional concept” heavily, such as map, each. They really depend on a self-contained function which is so called block in Ruby.
It is very common to loop though a 2d array, make an string about the elements.
In java, it may looks like
public String toString(){
String output = "[";
for (int i =0; i<array.length; i++) {
output+= "Row "+(i+1)+" : ";
for (int j=0; j<array[0].length;j++ ) {
output += array[i][j]+", ";
}
output += "\n";
}
return output += "]";
}
I tried to rewrite such a thing in “Ruby functional Style”, but I think there are still some improvements. Eg. I want to remove the mutable variable output
def to_s
output = "[\n"
@data.each_with_index do |row,i|
output << "Row #{i+1} : "
row.each { |num| output << "#{num}," }
output << "\n"
end
output+"]"
end
Whenever you see the pattern:
output)that’s a
fold, or in Ruby terms aninject.Actually, that’s a bit of a tautology. A
foldis a universal method of iteration: everything that can be expressed by iterating over the elements of a collection can also be expressed as afoldover the collection. In other words: all methods onEnumerable(includingeach!) could also be defined in terms ofinjectas the primitive method instead ofeach.Think about it this way: a collection can either be empty or there can be a current element. There’s no third option, if you cover those two cases, then you have covered everything. Well,
foldtakes two arguments: one which tells it what to do when the collection is empty, and one which tells it what to do with the current element. Or, put yet another way: you can see a collection as a series of instructions andfoldis an interpreter for those instructions. There are only two kinds of instructions: theENDinstruction and aVALUE(el)instruction. And you can supply the interpreter code for both those instructions to thefold.In Ruby, the second argument is not part of the argument list, it is the block.
So, what’s it look like as a
fold?If you’re curious about whether or not the
each_with_indexmay infect your code with some non-functional impurity, rest assured that you can just as easily get rid of it by including the index in the accumulator:Also note that in the first case, with the
each_with_index, we’re not actually doing anything “interesting” with the accumulator, unlike the second case, where we are using it to keep count of the index. In fact, the first case is actually a restricted form offold, it doesn’t use all of its power. It really is just amap:In my personal opinion, it would actually be perfectly okay to use (mutable) string appending here instead of string concatenation:
This saves us from creating a couple of unnecessary string objects, but more importantly: it is more idiomatic. The real problem is shared mutable state, but we’re not sharing our mutable string here: when
to_sreturns its caller does get access to the string, butto_sitself has returned and thus no longer has access to it.If you want to get real fancy, you could even use string interpolation:
Unfortunately, this not only breaks IRb’s syntax highlighting, but also my brain’s 😉