I have a neglectful relationship with inject. That is, I neglect to remember that it exists, having worked for so long with other languages which are “unfamiliar with the concept”. Amos King’s blog entry on Inject & Me – BFFs got me to thinking that ucat (see cat on steroids (or cat on a hot ruby roof)) could use inject as opposed to the each_byte. So, instead of
def non_printing(line)
proc = ""
line.each_byte do |c|
proc += case c
when (0 .. 8): "^#{(c + 64).chr}"
when (10 .. 11): "^#{(c + 64).chr}"
when (13 .. 26): "^#{(c + 64).chr}"
when (27 .. 31): "^#{%w([ \\ ] ^ _)[c - 27]}"
when 127: "^?"
when ((c & 128) == 128): "M-0#{c.to_s(8)}"
else c.chr
end
end
proc
end
I can do:
def non_printing(line)
line.split("").map{|c|c[0]}.inject("") do |s,c|
s += case c
when (0 .. 8): "^#{(c + 64).chr}"
when (10 .. 11): "^#{(c + 64).chr}"
when (13 .. 26): "^#{(c + 64).chr}"
when (27 .. 31): "^#{%w([ \\ ] ^ _)[c - 27]}"
when 127: "^?"
when ((c & 128) == 128): "M-0#{c.to_s(8)}"
else c.chr
end
end
end
I still think there needs to be a better way — going from string to an array of strings mapped to an array of bytes so that I can process it via inject seems to be awkward. So, I do some searching and find Object#enum_for (let me plug gotAPI — it’s a great tool for searching a large number of API’s) and come up with:
def non_printing(line)
line.enum_for(:each_byte).inject("") do |s,c|
s += case c
when (0 .. 8): "^#{(c + 64).chr}"
when (10 .. 11): "^#{(c + 64).chr}"
when (13 .. 26): "^#{(c + 64).chr}"
when (27 .. 31): "^#{%w([ \\ ] ^ _)[c - 27]}"
when 127: "^?"
when ((c & 128) == 128): "M-0#{c.to_s(8)}"
else c.chr
end
end
end
That seems cleaner to me. One of the things I love about ruby is that there’s usually more way than one to do something. And it’s often quicker, like in Unix, to go with what you know rather than making it more elegant. However, I also like that it’s easy to write elegant code.
And elegant code is a thing of beauty.
You can download the updated version of ucat.rb (you may need to rename it to ucat.rb)