Nuances of where to define a variable in ruby code - ruby

I've just started learning ruby, and the position of where variables are defined somewhat elude me. For example, why does this code work:
def two_sum(nums)
result = nil
i = 0
while i < nums.length
k = (nums.length - 1)
if nums[i] + nums[k] == 0
result = [i,k]
end
i += 1
k -= 1
end
return result
end
And why does this code not work:
def two_sum(nums)
result = nil
i = 0
k = (nums.length - 1)
while i < nums.length
if nums[i] + nums[k] == 0
result = [i,k]
end
i += 1
k -= 1
end
return result
end
Thank you in advance!

I think you code might just have a bug
while i < nums.length
k = (nums.length - 1)
...
k -= 1 # this statement has no effect!
end
Above, the value if k is always (nums.length - 1) because you reassign it at the begin of each iteration. The other statement has no effect.
k = (nums.length - 1)
while i < nums.length
...
k -= 1
end
Above, the value of k starts at (nums.length - 1) in the first iteration and is then reduced by 1 for each iteration.
Pro tipp —
It is very unusual in Ruby to use a for/while/until loop. If you want to loop over all elements use each or each_with_index instead
array.each { |each| ... }
array.each_with_index { |each, n| ... }

Related

undefined method `<' for nil:NilClass error but no nil exists?

Was wondering why I get the error: "undefined method `<' for nil:NilClass" when compiling. After looking for reasons why, I found that you cannot use [] on an object with nil as a value. This makes sense, but I don't see why my array would contain nil in it. What am I missing?
def binary_search(n, arr)
middle = arr.length #pick middle value
i = 0
j = arr.length - 1
while i <= j
if arr[middle] == n
return true
elsif arr[middle] < n
i = middle + 1
middle = (i + j) / 2
else
j = middle - 1
middle = (i + j) / 2
end
end
false
end
nums = [76,32,50,90,10,8,15,49]
nums.sort
puts nums.inspect
binary_search(50, nums)
Let's look at a simplified subset of the code:
arr = [76,32,50,90,10,8,15,49]
middle = arr.length # 8
arr[middle] < 50 # NoMethodError
The length is 8.
arr[8] is nil, because there is no item at index 8. Remember that Ruby indexes begin with 0.
nil < 50 is a NoMethodError

Ruby longest palindrome - why does a slight modification in a while loop break my code?

I'm trying to solve this Ruby problem and I can't figure out why having a minor while loop difference renders one test false: longest_palindrome("abba") outputs "bb" instead of "abba", which is false. I can only solve it with for and while loops, so please no advanced methods. It's easier to highlight the difference in the code block (first one is the working solution, second is mine. Also assume the palindrome? method is already defined):
def longest_palindrome(string)
best_palindrome = nil
idx1 = 0
***while idx1 < string.length
length = 1
while (idx1 + length) <= string.length
substring = string.slice(idx1, length)***
if palindrome?(substring) && (best_palindrome == nil || substring.length > best_palindrome.length)
best_palindrome = substring
end
length += 1
end
idx1 += 1
end
return best_palindrome
end
def longest_palindrome(string)
longest = nil
i = 0
***while i < string.length
i2 = 1
while i2 < string.length***
if palindrome?(string.slice(i, i2)) == true && (longest == nil || string.slice(i, i2).length > longest.length)
longest = string.slice(i, i2)
end
i2 += 1
end
i += 1
end
return longest
end
This part of your code...
while i2 < string.length
... means you're never checking the maximum possible length.
"abba".slice(0,4) is the entire string, but you only ever go up to "abba".slice(0,3) which is "abb".
So you never test the entire string.
Change the line to...
while i2 <= string.length
...and it should be ok.

Basic ruby code not working

I just learnt Ruby and I am doing some basic practise problems to get familiar with the language. I just came across this question:
"Write a method that takes an array of numbers. If a pair of numbers
in the array sums to zero, return the positions of those two numbers.
If no pair of numbers sums to zero, return nil."
This is my code:
def two_sum(nums)
i = 0
j = 0
while i < nums.length
while j < nums.length
if nums[i] + nums[j] == 0
return [i, j]
end
j+= 1
end
i += 1
end
return nil
end
It returns nil everytime. What's wrong with it?
j should be re-initialized to 0 when i gets incremented:
while i < nums.length
while j < nums.length
if nums[i] + nums[j] == 0
return [i, j]
end
j+= 1
end
i += 1
j = 0 # Here
end
While Yu Hao answers the literal question, here is a more Rubyish solution:
def two_sum(nums)
nums.each_with_index do |x, i|
j = nums.index(-x)
return [i, j] if j
end
nil
end

Why does one of these Ruby methods result in 'stack overflow' but the other doesn't? (Permutations Algorithm)

Below are two slightly different methods for listing all lexicographic permutations of N objects. I can't understand why the first method works fine for smallish N, but fails above a certain limit and results in 'stack overflow'. The second method; however, works just fine up to my tested limit of 10**6. Thanks in advance for your help and insight!
$count = 0
$permutations = []
def perms(array)
$permutations = array
$count += 1
if array.length <= 1
return $permuations
end
i = (array.length - 2)
until array[i] < array[i+1]
i -= 1
end
if i < 0
return $permutations
end
j = (array.length - 1)
until array[j] > array[i]
j -= 1
end
array[i], array[j] = array[j], array[i]
i += 1
j = (array.length - 1)
until j < i
array[i], array[j] = array[j], array[i]
i += 1
j -= 1
end
while $count <= (10**6)-2
perms(array)
end
end
perms([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print $permutations
And here's the second method...
perm_limit = (10**6)
$count = 1
def perms(array)
if array.length <= 1
return false
end
i = (array.length - 2)
until array[i] < array[i+1]
i = (i - 1)
end
if i < 0
return false
end
j = (array.length - 1)
until array[j] > array[i]
j = (j - 1)
end
array[i], array[j] = array[j], array[i]
i = (i + 1)
j = (array.length - 1)
until j < i
array[i], array[j] = array[j], array[i]
i = (i + 1)
j = (j - 1)
end
$count += 1
return true
end
array = [0,1,2,3,4,5,6,7,8,9]
while perms(array) == true
if $count == perm_limit
print array
end
end
Again, thanks.
The first code sample you provide is a recursive function:
while $count <= (10**6)-2
perms(array)
end
The function is calling itself, is calling itself, is calling itself until your stack overflows (everytime a function is called, space on stack is allocated).
Your second algorithm does not use a recursive function and so the depth of your stack is only one - your stack is not growing.
For more information see "What is a stack overflow". The question is for Java, but the concept is the same for all stack-based languages.
Recursive vs. iterative
So why are we writing recursive functions/algorithms if they can overflow? Because recursion can model some problems very nicely, and it can be easier to write a recursive algorithm (it's considered more "mathematically beautiful") than an iterative one. If you know that your recursion depth won't be too deep, then recursion might be the preferred method.
On the other hand, an iterative algorithm is usually the preferred if you're worried about your stack space. An iterative function can be easier or harder to write depending on how you model the problem. All recursive functions can be converted to iterative functions.
On a side note: there are some languages where recursion and stack space is not a problem. These languages may use "tail-calls" meaning the function is recursive, but instead of allocating new space on the stack, it simply re-uses the current function's stack space (and so the stack never grows). You can read more here.

Can I have a block in begin/while statement in Ruby?

I'm trying out to have a block in a while and begin statements in Ruby, but I get a syntax error. Any other way to implement it?
Here's what I want to accomplish
(1..limit).each { |i|
while (true) do |n|
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end
}
while is a keyword, so you do not need the block. Your code should be:
(1..limit).each { |i|
while (true)
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end
}
But you are requesting a block variable form the while statement. Variable names inside the pipes are for variables passed to your block containing information from whatever calls your block. I will assume that n is supposed to increment. Here is a working version:
(1..limit).each { |i|
n = 0
while (true)
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end
}
If you really need the code in a block, you could create one and then call it, like this (ruby 1.9 only):
(1..limit).each { |i|
n = 0
while (true)
-> do
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end.()
end
}
By the way, here is a cleaner version:
(1..limit).each do |i|
n = 0
loop do
x = n * (i % n)
puts n if n % i != 0
break if x.even? and !x.zero?
n += 1
end
end

Resources