This instructions are as follow:
NUMBER CRUNCHER
Write a method that takes a number as an argument
If the number is greater than 20
count down from the number by 2's
If the number is less than 20
count down from the number by 1's
Display the numbers as they count down to 0.
I have written this, but it's not doing what it's supposed. Any help?
def num_cruncher(num)
count = num
until count == 0 do
if num > 20
puts count - 2
else
puts "#{count}"
end
count -= 1
end
end
Here's your code, with as few changes as possible :
def num_cruncher(num)
count = num
until count < 0 do
puts count
if num > 20
count -= 2
else
count -= 1
end
end
end
num_cruncher(10)
# 10
# 9
# 8
# 7
# 6
# 5
# 4
# 3
# 2
# 1
# 0
num_cruncher(21)
# 21
# 19
# 17
# 15
# 13
# 11
# 9
# 7
# 5
# 3
# 1
By extracting the if-statement outside of the loop, the code becomes a bit shorter :
def num_cruncher(num)
if num > 20
step = 2
else
step = 1
end
until num < 0 do
puts num
num -= step
end
end
You can use Numeric#step here. Something like this:
def num_cruncher n
s = n > 20 ? -2 : -1
n.step(by: s, to: 0).entries
end
num_cruncher 23
#=> [23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1]
Related
Newbie here! Trying to implement a program to print the first 20 Fibonacci numbers in Ruby. I've managed to create a program which generates the nth number, but I want to produce all from 0 through to 20.
Is there a simple way to do this or do I need to rewrite the whole program?
CURRENT CODE
def fib(n)
if n < 1
return 0
elsif n == 1
return 1
else fib(n-2) + fib(n-1)
end
end
puts fib(20)
CURRENT OUTPUT EXAMPLE
6765
DESIRED OUTCOME
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
At the moment you only print the last value returned by your method (fib(20)) but not the result of all intermediate steps.
An easy way would be to cache all intermediate results in a hash data structure. This would also improve performance for big n because you do not need to recalculate many values over and over again.
Then you can just print out all results from 0 to n:
def cached_fib(n)
#cache ||= Hash.new do |cache, n|
#cache[n] = n < 2 ? n : cache[n-1] + cache[n-2]
end
#cache[n]
end
def fib(n)
0.upto(n) { |i| puts cached_fib(i) }
end
fib(20)
#=> 0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
Printing each value is easier with a button-up approach where you start at 0 and 1 and calculate each following value based on its predecessors, e.g.:
i, j = 0, 1
puts i
puts j
21.times do
k = i + j
puts k
i, j = j, k
end
You could turn the above into an Enumerator:
fib = Enumerator.new do |y|
i, j = 0, 1
y << i
y << j
loop do
k = i + j
y << k
i, j = j, k
end
end
Which will generate the sequence:
fib.take(21)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
# 233, 377, 610, 987, 1597, 2584, 4181, 6765]
I have this method which switches the number digit 5 with 7.
def switch_digit(num)
if num <= 0
return 0
end
digit = num % 10
if (digit == 5)
digit = 7
end
return switch_digit(num/10) * 10 + digit
end
switch_digit(5952)
Can someone explain why once the method hits the base case it doesn't return 0?
How does this recursive method actually work? Does it append the returned digit with the next digit?
I added a little change to your code, to be aware it's working.
Finally I also expected the value of the method was 0, but it is not.
The end is reached, but the returned value is not 0. Why?
def switch_digit(num, array)
if num <= 0
array << num
p array
puts "The end"
return 0
end
digit = num % 10
array << [digit, num]
if (digit == 5)
digit = 7
end
return p switch_digit(num/10, array) * 10 + digit
end
p "returned value = " + switch_digit(123456789, Array.new).to_s
Which outputs:
#=> [[9, 123456789], [8, 12345678], [7, 1234567], [6, 123456], [5, 12345], [4, 1234], [3, 123], [2, 12], [1, 1], 0]
#=> The end
#=> 1
#=> 12
#=> 123
#=> 1234
#=> 12347
#=> 123476
#=> 1234767
#=> 12347678
#=> 123476789
#=> "returned value = 123476789"
The base case returns 0, but the overall result is determined by the return switch_digit(num/10) * 10 + digit
Follow the code through with a smaller example e.g. switch_digit(15):
num <= 0 # no
digit = num % 10 # 5
digit == 5 # yep, so swap it for a 7
return switch_digit(num/10) * 10 + 7
num/10 is 1 so what does the recursive switch_digit(1) evaluate to?
num <= 0 # no
digit = num % 10 # 1
digit == 1 # so leave it unchanged
return switch_digit(num/10) * 10 + 1
num/10 is 0 so now we hit the base case
switch_digit(15) == switch_digit(1) * 10 + 7
switch_digit(1) == switch_digit(0) * 10 + 1
switch_digit(0) == 0 # the base case
working back up, plugging in values from lower down results:
switch_digit(1) == 0 * 10 + 1 == 1
switch_digit(15) == 1 * 10 + 7 == 17
I'd also add that there's nothing specific to Ruby about how recursion is handled here. Any other descriptions of recursion, or classic example such as a recursive factorial function should help you get a better understanding.
I have this code:
1 #!/local/usr/bin/ruby
2
3 users = (1..255).to_a
4
5 x = " "
6 y = " "
7 z = " "
8 #a = " "
9
10 count = 1
11 users.each do |i|
12 x << i if count == 1
13 y << i if count == 2
14 z << i if count == 3
15 # if x.length == 60
16 # a << i if count == 1
17 # a << i if count == 2
18 # a << i if count == 3
19 # else
20 # end
21 if count == 3
22 count = 1
23 else
24 count += 1
25 end
26 end
27
28 puts x.length
29 puts y.length
30 puts z.length
31 #puts a.length
32
What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
IT WORKS
Example of working code:
[user#server ruby]$ ruby loadtest.rb
86
86
86
[user#server ruby]$
Now what I want it to do is have a failsafe called a as seen above, commented out, What I want is this, if each string contains 60 numbers I want it to append into the a string until there are no more numbers.
When I try to do it with the commented out section it outputs this:
[user#server ruby]$ ruby loadtest.rb
86
86
86
4
[user#server ruby]$ ruby loadtest.rb
WHY?! What am I doing wrong?
What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
After reducing the number of values being iterated for readability, here's what it's doing:
users = (1..5).to_a
x = " "
y = " "
z = " "
count = 1
users.each do |i|
x << i if count == 1 # => " \u0001", nil, nil, " \u0001\u0004", nil
y << i if count == 2 # => nil, " \u0002", nil, nil, " \u0002\u0005"
z << i if count == 3 # => nil, nil, " \u0003", nil, nil
if count == 3
count = 1
else
count += 1
end
end
x # => " \u0001\u0004"
y # => " \u0002\u0005"
z # => " \u0003"
puts x.length
puts y.length
puts z.length
# >> 3
# >> 3
# >> 2
Your code is creating binary inside the strings, not "numbers" as we normally think of them, as digits.
Moving on, you can clean up your logic using each_with_index and case/when. To make the results more readable I switched from accumulating into strings into arrays:
users = (1..5).to_a
x = []
y = []
z = []
users.each_with_index do |i, count|
case count % 3
when 0
x << i
when 1
y << i
when 2
z << i
end
end
x # => [1, 4]
y # => [2, 5]
z # => [3]
puts x.length
puts y.length
puts z.length
# >> 2
# >> 2
# >> 1
The real trick in this is the use of %, which does a modulo on the value.
... if each string contains 60 numbers I want it to append into the a string until there are no more numbers
As written, you are unconditionally appending to x,y,z even after they hit your limit.
You need to add a conditional around this code:
x << i if count == 1
y << i if count == 2
z << i if count == 3
so that it stops appending once it hits your limit.
By the looks of the else block that does nothing, I think you were headed in that direction:
if x.length == 60
a << i if count == 1
a << i if count == 2
a << i if count == 3
else
x << i if count == 1
y << i if count == 2
z << i if count == 3
end
Even that, though, won't do exactly what you want.
You'll want to check the string you are appending to to see if it has hit your limit yet.
I'd suggest refactoring to make it cleaner:
users.each do |i|
target_string = case count
when 1 then x
when 2 then y
when 3 then z
end
target_string = a if target_string.length == 60
target_string << i
if count == 3
count = 1
else
count += 1
end
end
It may be better to use an array instead of string as you are pushing numbers into those variables.
Let me propose a solution which achieves more or less what you are trying to do, but uses few Ruby tricks that may be useful in future.
x, y, z = r = Array.new(3) {[]}
a = []
iter = [0,1,2].cycle
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
p x.size, y.size, z.size
p a.size
Let's define our arrays. Even though I have arrays x, y, and z, they are there only because they were present in your code - I think we just need three arrays, each of which would collect numbers as they are picked from a range of numbers - between 1 to 255 - one by one. x,y,z = r uses parallel assignment technique and is equivalent to x,y,z = r[0],r[1],r[2]. Also, use of Array.new(3) {[]} helps in creating the Array of Array such that when we access r[1] it is initialized with empty array([]) by default.
x, y, z = r = Array.new(3) {[]}
a = []
In order to determine which array the next number picked from range has to be placed in, we will use an Enumerator generated from Enumerable#cycle. This enumerator is special - because it is soft of infinite in nature - and we can keep asking it to give an element by calling next, and it will cycle through the array elements of [0,1,2] - returning us 0,1,2,0,1,2,0,1,2... infinitely.
iter = [0,1,2].cycle
Next, we will iterate through the range of numbers 1..255. During each iteration, we will check whether all the 3 arrays in which we are collecting number have desired size of 60 with the help of Enumerable#all? - if so, we will append the number to array a - else we will assign it to one of the sub arrays of r based on the array index returned by iter enumerator.
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
Finally, we print the size of each of the array.
p x.size, y.size, z.size
#=> 60, 60, 60
p a.size
#=> 75
Why is the output the same?
First inject:
puts (3...10).inject(0) { |sum, x| (x % 3 == 0 || x % 5 == 0) ? sum + x : sum }
# => 23
Second inject:
puts (3...10).inject { |sum, x| (x % 3 == 0 || x % 5 == 0) ? sum + x : sum }
# => 23
# Why not 26?
I thought if there is no argument passed to it, inject uses the first element of the collection as initial value.
So the second inject should return the same value as this one:
puts (3...10).inject(3) { |sum, x| (x % 3 == 0 || x % 5 == 0) ? sum + x : sum }
# => 26
Why does these two injects give the same output in ruby?
... Because they're supposed to. They only differ by the addition of a 0.
I thought if there is no argument passed to it, inject uses the first element of the collection as initial value.
It does. But it doesn't duplicate it.
Your first example receives these numbers:
0, 3, 4, 5, 6, 7, 8, 9
Your second example receives these numbers:
3, 4, 5, 6, ...
Adding 0 to the beginning doesn't affect the result, they're both 23, not 26 as you claim.
Your 3rd example returns 26 because it receives these numbers:
3, 3, 4, 5, 6, ...
#inject() with an argument:
result = (3...10).inject(0) do |sum, x|
puts "#{sum} <-- #{x}: sum = #{sum+x}"
sum + x
end
--output:--
0 <-- 3: sum = 3
3 <-- 4: sum = 7
7 <-- 5: sum = 12
12 <-- 6: sum = 18
18 <-- 7: sum = 25
25 <-- 8: sum = 33
33 <-- 9: sum = 42
...
#inject without an argument:
result = (3...10).inject() do |sum, x|
puts "#{sum} <-- #{x}: sum = #{sum+x}"
sum + x
end
--output:--
3 <-- 4: sum = 7
7 <-- 5: sum = 12
12 <-- 6: sum = 18
18 <-- 7: sum = 25
25 <-- 8: sum = 33
33 <-- 9: sum = 42
I always thought it takes the first element of the collection as
initial value and still performs the first iteration
The first iteration uses arr[0] as the sum and arr[1] as the first x. When you don't provide an argument for inject(), it's equivalent to doing this:
data = (3...10).to_a
initial_sum = data.shift
data.inject(initial_sum) do |sum, x|
puts "#{sum} <-- #{x}: sum = #{sum+x}"
sum + x
end
--output:--
3 <-- 4: sum = 7
7 <-- 5: sum = 12
12 <-- 6: sum = 18
18 <-- 7: sum = 25
25 <-- 8: sum = 33
33 <-- 9: sum = 42
New to StackOverflow here. I'm working on the first Euler problem and have run into an issue where I can get the statement to iterate through the array. It seems like it has something to do with the way I have the while loop setup but I can't figure it out.
Here's my code:
#euler problem 1
numbers = [3,5]
sum = 0
i=1
total=0
numbers.each do |number|
while i * number < 10
adder = i * number
total += adder
i += 1
puts total
end
end
puts total
The output is 3
9
18
18
Any idea why it isn't processing the 5 in the array numbers?
Your problem is that i is declared outside the block so when number is five, i is already four and the while loop's condition fails immediately because 20 < 10 is false. Try it like this:
numbers = [3,5]
sum = 0
total=0
numbers.each do |number|
i = 1
while i * number < 10
#...
end
end
puts total
If you put a little puts in your code you'll see what's going on:
i = 1
numbers.each do |number|
puts "#{number}\ti = #{i}"
while i * number < 10
puts "\ti = #{i}"
adder = i * number
total += adder
i += 1
end
end
That will give you this output:
3 i = 1
i = 1
i = 2
i = 3
5 i = 4
and you'll see the problem with i.