Champer Wu
Champer Wu

Reputation: 1271

ruby function with loop return the wrong value

I have a problem about the code

@nums = [3, 2, 4]
@target = 6

def two_sum(nums, target)
  random1 = nums.sample
  random2 = nums.sample
  ary1 = [nums.index(random1), nums.index(random2)].sort
  if random1 + random2 == target
   return ary1
  else
    return 1
  end
end

loop do
  if two_sum(@nums, @target) != 1
    print two_sum(@nums, @target)
    break
  end
  puts 2
end

This code should return the indices of two elements in [3, 2, 4] whose sum is 6, i.e. either [1, 2] or [0, 0]. But occasionally 1 appears, although it shouldn't.

Why does it happen?

Upvotes: 1

Views: 115

Answers (2)

Stefan
Stefan

Reputation: 114228

Your method's return value depends on a call to sample, i.e. it returns a random – and therefore potentially different – element each time. The method name two_sum however does not indicate its impure nature. If the method was called random_two_sum, the bug would have been a lot more obvious:

if random_two_sum(@nums, @target) != 1
  print random_two_sum(@nums, @target)
  break
end

So unless you want that randomness, a better approach is to yield the values one after another using an Enumerator:

def each_two_sum_index(nums, target)
  return enum_for(__method__, nums, target) unless block_given?
  nums.each_index.to_a.repeated_combination(2) do |i, j|
    yield i, j if nums[i] + nums[j] == target
  end
end

You can then invoke the method with a block instead of using an explicit loop:

each_two_sum_index([3, 2, 4], 6) do |i, j|
  p i: i, j: j
end

Output:

{:i=>0, :j=>0}
{:i=>1, :j=>2}

Upvotes: 1

mikej
mikej

Reputation: 66333

In this part of the code:

if two_sum(@nums, @target) != 1
  print two_sum(@nums, @target)

you are making two separate calls to the two_sum function, once in the if and then a second time in the print. So if the first result isn't equal to 1 you'll make a second call (which could return 1) and print the result from that second call.

Instead, you could capture the result of a single call to two_sum and print that e.g.

loop do
  result = two_sum(@nums, @target)
  if result != 1
    print result
  ...

or combining the assignment and comparison if you prefer:

if (result = two_sum(@nums, @target)) != 1
...

Upvotes: 4

Related Questions