I previously lamented this code:

  def parenthesized_list(array)
    list = "("
    first = false

    array.each do |element|
      if (first)
        list += ","
      else
        first = true
      end

      list += yield element
    end

    list + ")"
  end

I still haven’t found a magic bullet in Enumerable or Array which will let me dramatically shrink it. But I have at least teased out some of the components; this is what I’m using for now:

  def parenthesized_list(array)
    array.process_and_interpose("(", ",", ")") { |element| yield element }
  end

  class Array
    def process_and_interpose(initial, middle, last)
      inject_with_index(initial) do |memo, element, i|
        memo + yield(element) + (i != length - 1 ? middle : last)
      end
    end
  end

  module Enumerable
    def inject_with_index(initial)
      result = initial

      each_with_index { |element, i| result = yield(result, element, i) }

      result
    end
  end

inject_with_index doesn’t seem like a crazy idea; process_and_interpose is a bit specialized, but that’s fine.

Is there some way I can shrink the implementation of inject_with_index? I get the feeling that there’s some sort of generalization staring at me there, but I can’t quite figure it out. If I’m just shrinking code, I could keep on storing in initial instead of introducing a new variable result; I’d want to rename the variable, though. Maybe this?

  module Enumerable
    def inject_with_index(memo)
      each_with_index { |element, i| memo = yield(memo, element, i) }

      memo
    end
  end

I don’t think I like that so much, though: naming the (non-block) argument memo instead of initial it makes it harder to figure out how it gets used. So I kind of prefer calling the argument initial at the start, and then renaming it to result in the body to reflect the implementation.

And of course parenthesized_list is funny in that it just wants to pass along the block that it’s been given, but has to create a new block to do that. That, I think, reflects one of Ruby’s warts: there’s this weird block/procedure distinction that doesn’t, as far as I can tell, buy you much. It’s nice to be able to write blocks on the fly, but why not require functions taking one to make the block argument explicit and get rid of yield? I’m not sure of all the implications, but I don’t think that Ruby’s current choice is the best.

Post Revisions:

There are no revisions for this post.