I read the intention of the problem "It calls the code block once for each contiguous run of equal items in the array" as meaning the example list is a touch misleading since it contains no subsequent runs of a digit so that you can ignore the structure of the input list and just count how many times any given item appears. If the example had been:
[5,9,3,4,4,4,7,8,8,5,5]
Then printing "5 appears 3 times" doesn't suggest itself as correct.
I haven't written any Ruby in quite a while so this may not be optimal but it's what I came up with:
require 'test/unit/assertions'
include Test::Unit::Assertions
module Enumerable
def runs
if empty?
[]
else
runs = [[first,1]]
self[1..-1].each do |item|
if item == runs.last.first
runs = runs[0..-2] + [[item,runs.last.last+1]]
else
runs = runs + [[item,1]]
end
end
runs
end
end
def each_run(&block)
runs.each(&block)
end
end
assert_equal( [], [].runs )
assert_equal( [[5,1],[9,1],[3,1],[4,3],[7,1],[8,2],[5,2]], [5,9,3,4,4,4,7,8,8,5,5].runs )
[5,9,3,4,4,4,7,8,8,5,5].each_run do |m,n|
puts "#{m} appears #{n} times"
end
Outputting
> 5 appears 1 times
> 9 appears 1 times
> 3 appears 1 times
> 4 appears 3 times
> 7 appears 1 times
> 8 appears 2 times
> 5 appears 2 times
When I thought about it in Clojure what I came up with was this:
(defn runs [l]
(reduce (fn [memo [x n :as e]]
(let [[last-x last-n] (last memo)]
(if (= x last-x)
(conj (vec (drop-last memo)) [x (inc last-n)])
(conj memo e))))
[] (map (fn [e] [e 1]) l)))
(runs [5 5 9 8 1 1 4 3 3 3 3 5 5 5 2 2])
=> [[5 2] [9 1] [8 1] [1 2] [4 1] [3 4] [5 3] [2 2]]