Does anyone know why this code:
sum=0
def get_sum n
return sum if n<1
sum=sum+n
get_sum(n-1)
end
get_sum 10
gives me this?
rb:3:in `get_sum': undefined local variable or method `sum' for main:Object (NameError)
from 1.rb:8:in `<main>'
The code makes perfectly sense, and you can understand what it is doing.
Normal variables declared outside a function are not accessible inside the function.
You could prefix sum with $ to make it a global variable (not usually a good idea.)
$sum=0
def get_sum n
return $sum if n<1
$sum=$sum+n
get_sum(n-1)
end
get_sum 10
#= 55
If you have a real test case where you want to do this, I can suggest a suitable approach if you want.
Rather than using a global variable, another method using recursion would be like this (limited by stack depth, I guess):
def get_sum(n)
return n if n < 1
n + get_sum(n - 1)
end
sum = get_sum(10)
If you ever use a Ruby implementation that offers tail call optimization (I don't know of any implementations that do), ProGNOMmers's method would be a bit nicer on the stack, but as is a quick test has both exceeding the maximum stack level around n = 9000 or so. Not that you should be recursing 9000 times or anything.
As #Dogbert wrote, normal variables declared outside a function are not accessible inside
the function.
This is an approach which doesn't use global variables (which are not suited for recursion):
def get_sum(n, sum = 0)
return sum if n<1
get_sum(n-1, sum+n)
end
get_sum(10) #=> 55
You never initialized the variable sum before using it.
You can totally avoid using that variable, since it does nothing:
def get_sum n
return 0 if n<1
get_sum(n-1) + n
end
get_sum 10
You might want to try this :
def get_sum n
return n if n == 0
return n + get_sum(n-1)
end
In this version you don't need to instanciate any global variable ( which is not a good idea ), and you actually perform a regressive sum :)
Related
I need to DRY this code but I don't know how.
I tried to dry the if condition but I don't know how to put the while in this.
def sum_with_while(min, max)
# CONSTRAINT: you should use a while..end structure
array = (min..max).to_a
sum = 0
count = 0
if min > max
return -1
else
while count < array.length
sum += array[count]
count += 1
end
end
return sum
end
Welcome to stack overflow!
Firstly, I should point out that "DRY" stands for "Don't Repeat Yourself". Since there's no repetition here, that's not really the problem with this code.
The biggest issue here is it's unrubyish. The ruby community has certain things it approves of, and certain things it avoids. That said, while loops are themselves considered bad ruby, so if you've been told to write it with a while loop, I'm guessing you're trying to get us to do your homework for you.
So I'm going to give you a couple of things to do a web search for that will help start you off:
ruby guard clauses - this will reduce your if-else-end into a simple if
ruby array pop - you can do while item = array.pop - since pop returns nil once the array is empty, you don't need a count. Again, bad ruby to do this... but maybe consider while array.any?
ruby implicit method return - generally we avoid commands we don't need
It's worth noting that using the techniques above, you can get the content of the method down to 7 reasonably readable lines. If you're allowed to use .inject or .sum instead of while, this whole method becomes 2 lines.
(as HP_hovercraft points out, the ternary operator reduces this down to 1 line. On production code, I'd be tempted to leave it as 2 lines for readability - but that's just personal preference)
You can put the whole thing in one line with a ternary:
def sum_with_while(min, max)
min > max ? -1 : [*(min..max)].inject(0){|sum,x| sum + x }
end
This is one option, cleaning up your code, see comments:
def sum_with_while(range) # pass a range
array = range.to_a
sum, count = 0, 0 # parallel assignment
while count < array.length
sum += array[count]
count += 1
end
sum # no need to return
end
sum_with_while(10..20)
#=> 165
More Rubyish:
(min..max).sum
Rule 1: Choose the right algorithm.
You wish to compute an arithmetic series.1
def sum_with_while(min, max)
max >= min ? (max-min+1)*(min+max)/2 : -1
end
sum_with_while(4, 4)
#=> 4
sum_with_while(4, 6)
#=> 15
sum_with_while(101, 9999999999999)
#=> 49999999999994999999994950
1. An arithmetic series is the sum of the elements of an arithmetic sequence. Each term of the latter is computed from the previous one by adding a fixed constant n (possibly negative). Heremax-min+1 is the number of terms in the sequence and (min+max)/2, if (min+max) is even, is the average of the values in the sequence. As (max-min+1)*(min+max) is even, this works when (min+max) is odd as well.
Suppose we want to write a code that prints all ways of selecting n out of m options.
I think, the programming language does not matter, but if I should state it, Python.
I put the assignments in a vector A. Do I better define A as a global variable or pass it to the function each time? Why?
def choose(ind, n):
if n == 0:
print(A)
return
elif len(A)<= ind:
return
else:
A[ind] = 1
choose(ind + 1, n - 1)
A[ind] = 0
choose(ind + 1, n)
Always prefer passing over mutating globals whenever feasible.
Say you have the following functions:
def some_fun1 (n):
return n + 1;
m = 1;
def some_fun2 ():
return m + 1
With the first function, you can load up your REPL and throw data at it just by passing it as an argument. Your testing of that pure function has 0 effect on the rest of the program, which makes testing significantly easier.
With the second function, any time you need to test it, you must manually set all the globals the function relies on, which could potentially affect the operation of other functions if they rely on the same globals. This makes testing harder, and for that reason, among others, mutating globals should be avoided.
I don't quite understand how to "initialize a multidimensional array to equal 1" as the initial for loops seem to suggest here. I haven't learned to properly read pseudocode, and I don't fully understand how this program works.
function countRoutes(m,n)
grid ← array[m + 1][n + 1]
for i = 0 to m do
grid[i][0] ← 1
end for
for j = 0 to n do
grid[0][j] ← 1
end for
for i = 1 to m do
for j = 1 to n do
grid[i][j] ← grid[i − 1][j] + grid[i][j − 1]
end for
end for
return grid[m][n]
end function
Thanks for your help!
This isn't hard to translate.. Ruby uses = instead of left arrow for assignment, and def instead of function to define a subroutine (which it calls a method instead of a function), but that's about it. Let's go through it.
function countRoutes(m,n)
That's beginning a function definition. In Ruby we use a method instead, and the keyword to define such a thing is def. It's also convention in Ruby to use snake_case for multiword names instead of camelCase:
def count_routes(m, n)
Now to create the grid:
grid ← array[m + 1][n + 1]
Ruby arrays are dynamic, so you don't normally need to specify the size at creation time. But that also means you don't get initialization or two-dimensionality for free. So what we have to do here is create an array of m+1 arrays, each of which can be empty (we don't need to specify that the sub-arrays need to hold n+1 items). Ruby's Array constructor has a way to do just that:
grid = Array.new(m+1) do [] end
Now the initialization. Ruby technically has for loops, but nobody uses them. Instead, we use iterator methods. For counting loops, there's a method on integers called times. But the pseudocode counts from 0 through m inclusive; times also starts at 0, but only counts up to one less than the invocant (so that way when you call 3.times, the loop really does execute "three times", not four). In this case, that means to get the behavior of the pseudocode, we need to call times on m+1 instead of m:
(m+1).times do |i|
grid[i][0] = 1
end
As an aside, we could also have done that part of the initialization inside the original array creation:
grid = Array.new(m+1) do [1] end
Anyway, the second loop, which would be more awkward to incorporate into the original creation, works the same as the first. Ruby will happily extend an array to assign to not-yet-existent elements, so the fact that we didn't initialize the subarrays is not a problem:
(n+1).times do |j|
grid[0][j] = 1
end
For the nested loops, the pseudocode is no longer counting from 0, but from 1. Counting from 1 through m is the same number of loop iterations as counting from 0 through m-1, so the simplest approach is to let times use its natural values, but adjust the indexes in the assignment statement inside the loop. That is, where the pseudocode starts counting i from 1 and references i-1 and i, the Ruby code starts counting i from 0 and references i and i+1 instead.
m.times do |i|
n.times do |j|
grid[i+1][j+1] = grid[i][j+1] + grid[i+1][j]
end
end
And the return statement works the same, although in Ruby you can leave it off:
return grid[m][n]
end
Putting it all together, you get this:
def count_routes(m, n)
grid = Array.new(m+1) do [1] end
(n+1).times do |j|
grid[0][j] = 1
end
m.times do |i|
n.times do |j|
grid[i+1][j+1] = grid[i][j+1] + grid[i+1][j]
end
end
return grid[m][n]
end
The notation grid[i][j] ← something means assigning something to the element of grid taking place on i-th line in j-th position. So the first two loops here suggest setting all values of the first column and the first row of the grid (correspondingly, the first and the second loops) to 1.
I'm trying to write a method that returns the nth prime number.
I've worked out a solution but the problem is in my method. I create a large array of numbers that seems to process super slow. (1..104729).to_a to be exact. I chose 104729 because the max n can be is 10000 and the 10000th integer is 104729. I'm looking for a way to optimize my method.
Is 104729 is too large a value? Is there a way to write this so that I'm not creating a large array?
Here's the method:
def PrimeMover(num)
def is_prime(x)
i = 0
nums = (2..x).to_a
while nums[i] < nums.max
if x % nums[i] != 0
i += 1
else
return false
end
end
return true
end
primes_arr = (3..104729).to_a.select {|y| is_prime(y)}
primes_arr[num]
end
require "prime"
def find_prime(nth)
Prime.take(nth).last
end
Combine Ruby's built-in prime library, and a lazy enumerator for performance:
require 'prime'
(1...100_000).lazy.select(&:prime?).take(100).to_a
Or simply, as highlighted by Arturo:
Prime.take(100)
You can use Ruby's built in #prime? method, which seems pretty efficient.
The code:
require 'prime'
primes_arr = (3..104729).to_a.select &:prime?
runs in 2-3 seconds on my machine, which I find somewhat acceptable.
If you need even better performance or if you really need to write your own method, try implementing the Sieve of Erathostenes. Here are some Ruby samples of that: http://rosettacode.org/wiki/Sieve_of_Eratosthenes#Ruby
Here's an optimal a trial division implementation of is_prime without relying on the Prime class:
A prime number is a whole number divisible only by 1 and itself, and 1 is not prime. So we want to know if x divides into anything less than x and greater than 1. So we start the count at 2, and we end at x - 1.
def prime?(x)
return false if x < 2
2.upto(x - 1) do |n|
return false if (x % n).zero?
end
true
end
As soon as x % n has a remainder, we can break the loop and say this number is not prime. This saves you from looping over the entire range. If all the possible numbers were exhausted, we know the number is prime.
This is still not optimal. For that you would need a sieve, or a different detection algorithm to trial division. But it's a big improvement on your code. Taking the nth up to you.
I am trying to multiply any number of unknown arguments together to make a total.
def multiply(*num)
num.each { |i| puts num * num}
end
multiply(2,3,4)
multiply(2,3,4,5,6,7)
Another attempt:
def multiply(num)
num.to_i
i = 0
while i < num.length
total = num * num
return total
end
end
multiply(2,3,4)
multiply(2,3,4,5,6,7)
I keep running into errors:
(eval):73: undefined local variable or method `num_' for main:Object (NameError)
from (eval):81
Some saying that Array needs to be Integer.
I've tried doing something I thought was suppose to be very simple program to write.
def multiply(*num) captures all arguments given to the method in an array. If you run multiply(1, 2, 3, 4) then num will equal [1, 2, 3, 4]. So in both your attempts you're trying to multiply a whole array with itself (num * num), which is not going to work.
Others have suggested solutions to how to write the multiply method correctly, but let me give you a correct version of your first attempt and explain it properly:
def multiply(*numbers)
result = 1
numbers.each { |n| result = result * n }
result
end
As in your attempt I capture all arguments in an array, I call mine numbers. Then I declare a variable that will hold the result of all the multiplications. Giving it the value of 1 is convenient because it will not affect the multiplications, but I could also have shifted off the first value off of the numbers array, and there are other solutions.
Then I iterate over all the numbers with each, and for each number I multiply it with the current result and store this as the new result (I could have written this shorter with result *= n).
Finally I return the result (the last value of a method is what will be returned by the method).
There are shorter ways of doing the same thing, others have suggested using numbers.reduce { |n, result| n * result }, or even numbers.reduce(:*), but even though they are shorter, they are pretty cryptic and I assume they don't really help you get things working.
What you want is the #inject method
def multiple(*nums)
nums.inject(:*)
end
Inject will combine all of the elements in nums, using the operation specified ( * in this case ).
You can try this:
num.reduce(1) {|x, y| x*y }