I have a coding challenge to reverse a an array with 5 elements in it. How would I do this without using the reverse method?
Code:
def reverse(array)
a
One thought :-
ary = ["a", 1, "apple", 8, 90]
ary.values_at(*(ary.size-1).downto(0))
# => [90, 8, "apple", 1, "a"]
ary.size.downto(0)
gives #<Enumerator: ...>
. And *#<Enumerator: ...>
is just a Enumerable#to_a method call which splats the Enumerator
to [4, 3, 2, 1, 0]
. Finally, Array#values_at
is working as documented.
def reverse(array)
array.values_at(*((array.size-1).downto 0))
end
Here's another non-destructive approach:
arr = ["a", 1, "apple", 8, 90]
arr.size.times.with_object([]) { |_,a| a << arr.rotate!(-1).first }
#=> [90, 8, "apple", 1, "a"]
arr
#=> ["a", 1, "apple", 8, 90]
Another would the most uninteresting method imaginable:
(arr.size-1).downto(0).with_object([]) { |i,a| a << arr[i] }
#=> [90, 8, "apple", 1, "a"]
arr
#=> ["a", 1, "apple", 8, 90]
Recursion indeed is the solution if you're not going to use a loop. while
or until
is still a loop, and using built-in methods not doing recursion may also still be using a loop internally.
#!/usr/bin/env ruby
a = [1, 2, 3]
def reverse(array)
t = array.pop
reverse(array) if array.length > 0
array.unshift t
end
puts reverse(Array.new(a)).inspect # [3, 2, 1]
Naturally recursion has limits since it depends on the stack but that's the best you can have if you don't want to use a loop. Following Cary Swoveland's post, this is the benchmark on 8500 elements:
user system total real
@Grych 0.060000 0.010000 0.070000 ( 0.073179)
@ArupRakshit 0.000000 0.000000 0.000000 ( 0.000836)
@konsolebox 0.000000 0.000000 0.000000 ( 0.001771)
@JörgWMittag recursion 0.050000 0.000000 0.050000 ( 0.053475)
@Jörg tail 0.210000 0.040000 0.250000 ( 0.246849)
@Jörg fold 0.040000 0.010000 0.050000 ( 0.045788)
@Jörg loop 0.000000 0.000000 0.000000 ( 0.000924)
Cary rotate 0.060000 0.000000 0.060000 ( 0.059954)
Cary boring 0.000000 0.000000 0.000000 ( 0.001004)
You can treat array as a stack and pop
the elements from the end:
def reverse(array)
rev = []
rev << array.pop until array.empty?
rev
end
or if you don't like modifying objects, use more functional-like reduce
:
def reverse(array)
array.reduce([]) {|acc, x| [x] + acc}
end
Cary mentioned in the comment about the performance. The functional approach might not be the fastest way, so if you really want to do it fast, create a buffor array and just add the items from the end to begin:
def reverse(array)
reversed = Array.new(array.count)
array.each_with_index do |item, index|
reversed[-(index + 1)] = item
end
reversed
end
Gentlemen, start your engines!
[Edit: added two method from @Grych and results for n = 8_000.]
@Grych, @ArupRakshit, @konsolebox and @JörgWMittag: please check that I've written your method(s) correctly.
Methods
def grych_reduce(array)
array.reduce([]) {|acc, x| [x] + acc}
end
def grych_prebuild(array)
reversed = Array.new(array.count)
array.each_with_index do |item, index|
reversed[-(index + 1)] = item
end
reversed
end
def arup(ary)
ary.values_at(*(ary.size-1).downto(0))
end
def konsolebox(array)
t = array.pop
konsolebox(array) if array.length > 0
array.unshift t
end
def jorg_recurse(array)
return array if array.size < 2
reverse(array.drop(1)) + array.first(1)
end
def jorg_tail(array, accum=[])
return accum if array.empty?
reverse(array.drop(1), array.first(1) + accum)
end
def jorg_fold(array)
array.reduce([]) {|accum, el| [el] + accum }
end
def jorg_loop(array)
array.each_with_object([]) {|el, accum| accum.unshift(el) }
end
def cary_rotate(arr)
arr.size.times.with_object([]) { |_,a| a << arr.rotate!(-1).first }
end
def cary_boring(arr)
(arr.size-1).downto(0).with_object([]) { |i,a| a << arr[i] }
end
Benchmark
require 'benchmark'
arr = [*(1..n)]
puts "n = #{n}"
Benchmark.bm(16) do |bm|
bm.report('grych_reduce') { grych_reduce(arr) }
bm.report('grych_prebuild') { grych_prebuild(arr) }
bm.report('arup') { arup(arr) }
bm.report('konsolebox') { konsolebox(arr) }
bm.report('jorg_recurse') { jorg_recurse(arr) }
bm.report('jorg_tail') { jorg_tail(arr) }
bm.report('jorg_fold') { jorg_fold(arr) }
bm.report('jorg_loop') { jorg_loop(arr) }
bm.report('cary_rotate') { cary_rotate(arr) }
bm.report('cary_boring') { cary_boring(arr) }
bm.report('grych_destructo') { grych_destructo(arr) }
end
Wednesday: warm-up (n = 8_000)
user system total real
grych_reduce 0.060000 0.060000 0.120000 ( 0.115510)
grych_prebuild 0.000000 0.000000 0.000000 ( 0.001150)
arup 0.000000 0.000000 0.000000 ( 0.000563)
konsolebox 0.000000 0.000000 0.000000 ( 0.001581)
jorg_recurse 0.060000 0.040000 0.100000 ( 0.096417)
jorg_tail 0.210000 0.070000 0.280000 ( 0.282729)
jorg_fold 0.060000 0.080000 0.140000 ( 0.138216)
jorg_loop 0.000000 0.000000 0.000000 ( 0.001174)
cary_rotate 0.060000 0.000000 0.060000 ( 0.056863)
cary_boring 0.000000 0.000000 0.000000 ( 0.000961)
grych_destructo 0.000000 0.000000 0.000000 ( 0.000524)
Thursday: trials #1 (n = 10_000)
user system total real
grych_reduce 0.090000 0.080000 0.170000 ( 0.163276)
grych_prebuild 0.000000 0.000000 0.000000 ( 0.001500)
arup 0.000000 0.000000 0.000000 ( 0.000706)
jorg_fold 0.080000 0.060000 0.140000 ( 0.139656)
jorg_loop 0.000000 0.000000 0.000000 ( 0.001388)
cary_rotate 0.090000 0.000000 0.090000 ( 0.087327)
cary_boring 0.000000 0.000000 0.000000 ( 0.001185)
grych_destructo 0.000000 0.000000 0.000000 ( 0.000694)
konsolebox
, jorg_recurse
and jorg_tail
eliminated (stack level too deep).
Friday: trials #2 (n = 50_000)
user system total real
grych_reduce 2.430000 3.490000 5.920000 ( 5.920393)
grych_prebuild 0.010000 0.000000 0.010000 ( 0.007000)
arup 0.000000 0.000000 0.000000 ( 0.003826)
jorg_fold 2.430000 3.590000 6.020000 ( 6.026433)
jorg_loop 0.010000 0.010000 0.020000 ( 0.008491)
cary_rotate 2.680000 0.000000 2.680000 ( 2.686009)
cary_boring 0.010000 0.000000 0.010000 ( 0.006122)
grych_destructo 0.000000 0.000000 0.000000 ( 0.003288)
Saturday: qualifications (n = 200_000)
user system total real
grych_reduce 43.720000 66.140000 109.860000 (109.901040)
grych_prebuild 0.030000 0.000000 0.030000 ( 0.028287)
jorg_fold 43.700000 66.490000 110.190000 (110.252620)
jorg_loop 0.030000 0.010000 0.040000 ( 0.030409)
cary_rotate 43.060000 0.050000 43.110000 ( 43.118151)
cary_boring 0.020000 0.000000 0.020000 ( 0.024570)
grych_destructo 0.010000 0.000000 0.010000 ( 0.013338)
arup_verse
eliminated (stack level too deep); grych_reduce
, jorg_fold
and cary_rotate
eliminated (uncompetitive).
Sunday: final (n = 10_000_000)
user system total real
grych_prebuild 1.450000 0.020000 1.470000 ( 1.478903)
jorg_loop 1.530000 0.040000 1.570000 ( 1.649403)
cary_boring 1.250000 0.040000 1.290000 ( 1.288357)
grych_destructo 0.640000 0.030000 0.670000 ( 0.689819)