Hannah Squier
Hannah Squier

Reputation: 21

Why is the word variable different inside and outside this while loop?

When running the following code, I get a surprising output. Why does the value of the word variable change before and after entering the while loop in the third call to the recursive function?

def get_perms(word)
  perms = []
  get_perm_recursive("",word, perms)
  perms
end

def get_perm_recursive(prefix, word, perms)
  puts "---------------------"
  if word.length == 0
    perms << prefix
  end

  i = 0
  puts "Word outside loop: #{word}"
  while i < word.length
    puts "Word inside loop: #{word}"
    prefix << word[i]
    new_word = word[0...i] + word[i+1..-1]
    get_perm_recursive(prefix, new_word, perms)
    i += 1
  end

end

 get_perms("ab")

Output:

---------------------
Word outside loop: ab
Word inside loop: ab
---------------------
Word outside loop: b
Word inside loop: b
---------------------
Word outside loop: 
Word inside loop: ab
---------------------
Word outside loop: a
Word inside loop: a
---------------------
Word outside loop: 

Upvotes: 2

Views: 71

Answers (2)

SteveTurczyn
SteveTurczyn

Reputation: 36860

It's not. You're misinterpreting from which iteration the outputs are being produced.

---------------------  # this is iteration 1
Word outside loop: ab # this is iteration 1
Word inside loop: ab # this is iteration 1
--------------------- # this is iteration 2
Word outside loop: b # this is iteration 2
Word inside loop: b # this is iteration 2
--------------------- # this is iteration 3
Word outside loop: # this is iteration 3
# Iteration 3 has NO FURTHER OUTPUT (because i is not less than word.length)
# we are returned to Iteration 2
# but... Iteration 2 ALSO has NO FURTHER OUTPUT (because i in that iteration, increased to 1, is not less than word length)
# we are returned to Iteration 1
Word inside loop: ab # this is the SECOND while loop in the first iteration, so yes, the word is "ab"

Here's a simple way to modify the output to see what iteration you're in... first iteration has an iteration argument that defaults to 1, and is incremented for each call to the next iteration:

def get_perm_recursive(prefix, word, perms, iteration=1)
  puts "---------------------"
  if word.length == 0
    perms << prefix
  end

  i = 0
  puts "Word outside loop: #{word} in iteration: #{iteration}"
  while i < word.length
    puts "Word inside loop: #{word} in iteration: #{iteration}""
    prefix << word[i]
    new_word = word[0...i] + word[i+1..-1]
    get_perm_recursive(prefix, new_word, perms, iteration + 1)
    i += 1
  end

end

Incidentally... it seems you're expecting get_perms to return an array of perms (permutations?). But you have no mechanism to return the perms calculated within the permutation calls. You need to ensure each method returns perms AND you need to assign the returned perms to a variable.

Change the first method to...

def get_perms(word)
  perms = []
  perms = get_perm_recursive("",word, perms)
  perms
end

...so that the result of the get_perm_recursive is assigned to a variable, or even better, just have the get_perm_recursive as the last executed statement.

def get_perms(word)
  get_perm_recursive("",word, [])
end

You'll also need to trap the output of get_perm_recursive WITHIN get_perm_recursive, so replace,

get_perm_recursive(prefix, new_word, perms)

with

perms = perms + get_perm_recursive(prefix, new_word, perms)

And the very LAST statement of the get_perm_recursive method should return the perms array...

    i += 1
  end
  perms

end

Another thing I'd mention, the structure

i = 0 
while i < limiting_value
  ...
  i += 1
end

...is not ruby-esque. A more typical and nicer implementation is

(0...limiting_value) do |i| 
  ...
end

Upvotes: 1

philomory
philomory

Reputation: 1767

You have a recursive function with a loop in it. Try adding some additional logging:

def get_perms(word)
  perms = []
  $level = 0
  get_perm_recursive("",word, perms)
  perms
end

def get_perm_recursive(prefix, word, perms)
  $level += 1
  puts "level: #{$level}"
  puts "entering get_perm_recursive(#{prefix},#{word},#{perms}"
  if word.length == 0
    perms << prefix
  end

  i = 0
  puts "Word outside loop: #{word}"
  while i < word.length
    puts "Word inside loop: #{word}, #{i}"
    prefix << word[i]
    new_word = word[0...i] + word[i+1..-1]
    puts "Calling gets_perm_recursive recursively"
    get_perm_recursive(prefix, new_word, perms)
    i += 1
  end
  puts "done with gets perm recursive #{$level}"

  $level -= 1
end

get_perms("ab")

Output:

level: 1
entering get_perm_recursive(,ab,[]
Word outside loop: ab
Word inside loop: ab, 0
Calling gets_perm_recursive recursively
level: 2
entering get_perm_recursive(a,b,[]
Word outside loop: b
Word inside loop: b, 0
Calling gets_perm_recursive recursively
level: 3
entering get_perm_recursive(ab,,[]
Word outside loop:
done with gets perm recursive 3
done with gets perm recursive 2
Word inside loop: ab, 1
Calling gets_perm_recursive recursively
level: 2
entering get_perm_recursive(abb,a,["abb"]
Word outside loop: a
Word inside loop: a, 0
Calling gets_perm_recursive recursively
level: 3
entering get_perm_recursive(abba,,["abba"]
Word outside loop:
done with gets perm recursive 3
done with gets perm recursive 2
done with gets perm recursive 1

Upvotes: 0

Related Questions