I'll move on from this topic soon, rest assured. Having come up with a CFML answer for my code puzzle question ("Weekend code puzzle: my answer (CFML version)"), I decided to stretch myself (albeit slightly) and work out how to do it in Ruby too.
For the sake of completeness, here it is.
# getSubseries.rb
def getSubseries(series, threshold)
working = []
series.inject [] do |reduction, current|
working.push current
working.shift while working.inject(0, &:+) > threshold
workingIsBetterForLength = working.length > reduction.length
workingIsBetterForTotal = working.length == reduction.length && working.inject(0, &:+) > reduction.inject(0, &:+)
workingIsBetterForLength || workingIsBetterForTotal ? working.clone : reduction
end
end
This is the same logic as the CFML one. Observations:
- the reduction / folding operation on Ruby is called inject for some reason.
- this indecipherable mess -
working.inject(0, &:+)
- is shorthand for this:
array.inject{|sum,x| sum + x }
IE: the Ruby equivalent of.sum()
. Ruby has no inbuilt method for summing arrays.
I have no idea what it means (and, lazily, I have not checked yet), or how that works. - Of my own code, I like this version the best. It's clearer code IMO.
- I like how Ruby method calls don't need parentheses, too.
# getSubseriesTest.rb
load "getSubseries.rb"
result = getSubseries [], 0
puts "Should return an array\n" unless result.is_a? Array
result = getSubseries [100], 100
puts "Should return elements if there are any within the threshold\n" unless result == [100]
result = getSubseries [100,100], 500
puts "Should return a multi-element subseries\n" unless result == [100,100]
result = getSubseries [100,100,100,100,100,100], 500
puts "Total of elements should not be greater than threshold\n" unless result.inject(0){|sum,x| sum + x } <= 500
result = getSubseries [600,100,100,100,600,100,100,100,100,600], 500
puts "Should return a subsequent larger subseries\n" unless result == [100,100,100,100]
result = getSubseries [600,100,100,100,600,200,100,100,100,100,100,600], 500
puts "Should return a longer adjacent subseries\n" unless result == [100,100,100,100,100]
result = getSubseries [600,700,800,900], 500
puts "Should work when threshold is less than all items\n" unless result == []
result = getSubseries [600,700,800,900], 1000
puts "Should work when threshold is greater than all items\n" unless result == [900]
result = getSubseries [600,700,800,900], 5000
puts "Should work when threshold is greater than sum of all items\n" unless result == [600,700,800,900]
result = getSubseries [100,50,50,50,50,50,500,100,60,60,60,60,60,500], 500
unless result == [100,60,60,60,60,60] then
puts "Should return subseries with highest tally when there are two equal-length subseries (second subseries is higher)\n"
end
result = getSubseries [100,60,60,60,60,60,500,100,50,50,50,50,50,500], 500
unless result == [100,60,60,60,60,60]
puts "Should return subseries with highest tally when there are two equal-length subseries (first subseries is higher)\n"
end
That's quite nice-looking code too.
Anyway, that's all I have to say on that. I had better crack on with some work.
If you know Ruby and see how I've messed things up, or could have done better: please let me know! I'm definitely a newbie when it comes to Ruby.
--
Adam