为什么当我传递一个数组的引用调用函数时,在Ruby中数组会改变?

huangapple go评论96阅读模式
英文:

why when I call a function passing a reference to an array, the array is changing in ruby?

问题

I was following a tutorial on creating a little game in Ruby when I fell into a bug I couldn't understand. When I looked closely, I realized that the error was happening in line 51 of the fogefoge.rb on the call of nova_posicao = calcula_nova_posicao heroi, direcao.

At this point of the tutorial, the hero "H" should be able to move around with the keys "ASDW". I noticed that the array heroi at line 49 heroi = encontra_jogador mapa has been changed and that was introducing the bug in the application. After that I created another variable called heroi2 and the code worked.

Why when I call line 51 of fogefoge.rb, the array is changing?

From what I've been reading in Rubydocs, I just can't understand why this is happening.

  1. File "main.rb":
  2. require_relative "fogefoge"
  3. inicia_fogefoge
  1. File "mapa1.txt":
  2. XXXXX
  3. X H X
  4. X X X
  5. X X X
  6. X X
  7. X
  8. XXX
  9. X
  10. X F X
  11. XXXXX
  1. File "ui.rb":
  2. def da_boas_vindas
  3. puts "bem vindo ao Foge-foge"
  4. puts "Qual é o seu nome?"
  5. nome = gets.strip
  6. puts "\n\n\n\n\n"
  7. puts "Começaremos o jogo para você, #{nome}"
  8. nome
  9. end
  10. def desenha(mapa)
  11. puts mapa
  12. end
  13. def pede_movimento
  14. puts "Para onde deseja ir?"
  15. movimento = gets.strip
  16. end
  1. #File "fogefoge.rb":
  2. require_relative "ui"
  3. def le_mapa(numero)
  4. arquivo = "mapa#{numero}.txt"
  5. texto = File.read(arquivo)
  6. mapa = texto.split "\n"
  7. # p mapa
  8. end
  9. def encontra_jogador(mapa)
  10. caractere_do_heroi = "H"
  11. mapa.each_with_index do |linha_atual, linha|
  12. coluna_do_heroi = linha_atual.index caractere_do_heroi
  13. if coluna_do_heroi
  14. return [linha, coluna_do_heroi]
  15. end
  16. end
  17. end
  18. def calcula_nova_posicao(heroi, direcao)
  19. case direcao
  20. when "W"
  21. heroi[0] -= 1
  22. when "S"
  23. heroi[0] += 1
  24. when "A"
  25. heroi[1] -= 1
  26. when "D"
  27. heroi[1] += 1
  28. end
  29. heroi
  30. end
  31. def posicao_valida?(mapa, posicao)
  32. linhas = mapa.size
  33. colunas = mapa[0].size
  34. estourou_linhas = posicao[0] < 0 || posicao[0] >= linhas
  35. estourou_colunas = posicao[1] < 0 || posicao[1] >= colunas
  36. if estourou_linhas || estourou_colunas
  37. return false
  38. end
  39. if mapa[posicao[0]][posicao[1]] == "X"
  40. return false
  41. end
  42. true
  43. end
  44. def joga(nome)
  45. mapa = le_mapa 1
  46. while true
  47. desenha mapa
  48. direcao = pede_movimento
  49. heroi = encontra_jogador mapa
  50. #heroi2 = encontra_jogador mapa
  51. nova_posicao = calcula_nova_posicao heroi, direcao #At this call
  52. if !posicao_valida? mapa, nova_posicao
  53. next
  54. end
  55. mapa[heroi[0]][heroi[1]] = " "
  56. mapa[nova_posicao[0]][nova_posicao[1]] = "H"
  57. end
  58. end
  59. def inicia_fogefoge
  60. nome = da_boas_vindas
  61. joga nome
  62. end

I was expecting that the "H" would move around through the blank spaces, but the "H" was repeated to a few directions.

OUTPUT:

  1. bem vindo ao Foge-foge
  2. Qual é o seu nome?
  3. marcos
  4. Começaremos o jogo para você, marcos
  5. XXXXX
  6. X H X
  7. X X X
  8. X X X
  9. X X
  10. X
  11. XXX
  12. X
  13. X F X
  14. XXXXX
  15. Para onde deseja ir?
  16. d
  17. XXXXX
  18. X H X
  19. X X X
  20. X X X
  21. X X
  22. X
  23. XXX
  24. X
  25. X F X
  26. XXXXX
  27. Para onde deseja ir?
  28. D
  29. XXXXX
  30. X HHX
  31. X X X
  32. X X X
  33. X X
  34. X
  35. XXX
  36. X
  37. X F X
  38. XXXXX
  39. Para onde deseja ir?
  40. S
  41. XXXXX
  42. X HHX
  43. X X X
  44. X X X
  45. X X
  46. X
  47. XXX
  48. X
  49. X F X

If you uncomment the second reference at line 50 and then change line 51 to nova_posicao = calcula_nova_posicao heroi2, direcao then the code works.

英文:

I was following a tutorial on creating a little game in Ruby when I fell into a bug I couldn't understand. When I looked closely, I realized that the error was happening in line 51 of the fogefoge.rb on the call of nova_posicao = calcula_nova_posicao heroi, direcao.<br><br>
At this point of the tutorial, the heroe "H" should be able to move around with the keys "ASDW". I noticed that the array heroi at line 49 heroi = encontra_jogador mapa has been changed and that was introducing the bug in the application. After that I created another variable called heroi2 and the code worked.<br><br>
Why when I call line 51 of fogefoge.rb, the array is changing?<br><br>
From what I've been reading in Rubydocs, I just can't understand why this is happening.

  1. File &quot;main.rb&quot;
  2. require_relative &quot;fogefoge&quot;
  3. inicia_fogefoge
  1. File &quot;mapa1.txt&quot;
  2. XXXXX
  3. X H X
  4. X X X
  5. X X X
  6. X X
  7. X
  8. XXX
  9. X
  10. X F X
  11. XXXXX
  1. File &quot;ui.rb&quot;
  2. def da_boas_vindas
  3. puts &quot;bem vindo ao Foge-foge&quot;
  4. puts &quot;Qual &#233; o seu nome?&quot;
  5. nome = gets.strip
  6. puts &quot;\n\n\n\n\n&quot;
  7. puts &quot;Come&#231;aremos o jogo para voc&#234;, #{nome}&quot;
  8. nome
  9. end
  10. def desenha(mapa)
  11. puts mapa
  12. end
  13. def pede_movimento
  14. puts &quot;Para onde deseja ir?&quot;
  15. movimento = gets.strip
  16. end
  1. #File &quot;fogefoge.rb&quot;
  2. require_relative &quot;ui&quot;
  3. def le_mapa(numero)
  4. arquivo = &quot;mapa#{numero}.txt&quot;
  5. texto = File.read(arquivo)
  6. mapa = texto.split &quot;\n&quot;
  7. # p mapa
  8. end
  9. def encontra_jogador(mapa)
  10. caractere_do_heroi = &quot;H&quot;
  11. mapa.each_with_index do |linha_atual, linha|
  12. coluna_do_heroi = linha_atual.index caractere_do_heroi
  13. if coluna_do_heroi
  14. return [linha, coluna_do_heroi]
  15. end
  16. end
  17. end
  18. def calcula_nova_posicao(heroi, direcao)
  19. case direcao
  20. when &quot;W&quot;
  21. heroi[0] -= 1
  22. when &quot;S&quot;
  23. heroi[0] += 1
  24. when &quot;A&quot;
  25. heroi[1] -= 1
  26. when &quot;D&quot;
  27. heroi[1] += 1
  28. end
  29. heroi
  30. end
  31. def posicao_valida?(mapa, posicao)
  32. linhas = mapa.size
  33. colunas = mapa[0].size
  34. estourou_linhas = posicao[0] &lt; 0 || posicao[0] &gt;= linhas
  35. estourou_colunas = posicao[1] &lt; 0 || posicao[1] &gt;= colunas
  36. if estourou_linhas || estourou_colunas
  37. return false
  38. end
  39. if mapa[posicao[0]][posicao[1]] == &quot;X&quot;
  40. return false
  41. end
  42. true
  43. end
  44. def joga(nome)
  45. mapa = le_mapa 1
  46. while true
  47. desenha mapa
  48. direcao = pede_movimento
  49. heroi = encontra_jogador mapa
  50. #heroi2 = encontra_jogador mapa
  51. nova_posicao = calcula_nova_posicao heroi, direcao #At this call
  52. if !posicao_valida? mapa, nova_posicao
  53. next
  54. end
  55. mapa[heroi[0]][heroi[1]] = &quot; &quot;
  56. mapa[nova_posicao[0]][nova_posicao[1]] = &quot;H&quot;
  57. end
  58. end
  59. def inicia_fogefoge
  60. nome = da_boas_vindas
  61. joga nome
  62. end

I was expecting that the "H" would move around through the blank spaces, but the "H" was repeated to a few directions<br><br>
<br>
OUTPUT:
<br>

  1. bem vindo ao Foge-foge
  2. Qual &#233; o seu nome?
  3. marcos
  4. Come&#231;aremos o jogo para voc&#234;, marcos
  5. XXXXX
  6. X H X
  7. X X X
  8. X X X
  9. X X
  10. X
  11. XXX
  12. X
  13. X F X
  14. XXXXX
  15. Para onde deseja ir?
  16. d
  17. XXXXX
  18. X H X
  19. X X X
  20. X X X
  21. X X
  22. X
  23. XXX
  24. X
  25. X F X
  26. XXXXX
  27. Para onde deseja ir?
  28. D
  29. XXXXX
  30. X HHX
  31. X X X
  32. X X X
  33. X X
  34. X
  35. XXX
  36. X
  37. X F X
  38. XXXXX
  39. Para onde deseja ir?
  40. S
  41. XXXXX
  42. X HHX
  43. X X X
  44. X X X
  45. X X
  46. X
  47. XXX
  48. X
  49. X F X

if you uncomment the second reference at line 50 and then change line 51 to nova_posicao = calcula_nova_posicao heroi2, direcao then the code work.

答案1

得分: 0

Your calcula_nova_posicao 实现会改变传入的 heroi 数组,然后返回它。因此,在调用后,nova_posicaoheroi 将相同(不仅包含相同的值,而且都引用同一个对象)。

以下是一个快速示例,说明如何验证这一点:(equal? 用于检查两个对象是否实际上是同一个对象)

  1. a = [0, 0]
  2. b = calcula_nova_posicao(a, "S")
  3. a #=> [1, 0] <- 请注意 "a" 已经改变!
  4. b #=> [1, 0]
  5. a.equal?(b) #=> true

为什么我在 Ruby 中调用一个传递数组引用的函数时,数组会发生更改?

这是 Ruby 中的一个非常基本的行为。某些对象是 可变的,也就是说,您可以通过向它们发送某些消息(所谓的 破坏性方法)来更改这些对象。可变对象的示例包括字符串、哈希和数组。不管消息是如何发送或由谁发送的,一旦接收到,对象就会自行更改。

以下是两个变量 ab 的示例,它们引用同一个字符串对象。如果向字符串发送 upcase! 消息,它将大写其字符,两个变量都将反映这种更改:(因为它们仍然引用相同已更改的对象)

  1. a = "foo"
  2. b = a
  3. a.upcase! # 或 b.upcase!
  4. a #=> "FOO"
  5. b #=> "FOO"

同样适用于方法参数。如果将可变对象传递给方法,方法可以像在方法外部一样更改它:

  1. def my_method(s)
  2. s.upcase!
  3. end
  4. a = "foo"
  5. my_method(a)
  6. a #=> "FOO"

回到您的问题。您可能希望 calcula_nova_posicao 1) 返回一个 数组,2) 不更改传入的数组。后者对于清除地图上当前的 H 非常重要。但一般来说,不在方法内更改参数是一个好主意,因为它可能导致意外的结果和难以找到的错误。

以下是一个实现:

  1. def calcula_nova_posicao(heroi, direcao)
  2. case direcao
  3. when "W"
  4. [heroi[0] - 1, heroi[1]]
  5. when "S"
  6. [heroi[0] + 1, heroi[1]]
  7. when "A"
  8. [heroi[0], heroi[1] - 1]
  9. when "D"
  10. [heroi[0], heroi[1] + 1]
  11. end
  12. end

以下是另一种使用 数组分解 的方法:

  1. def calcula_nova_posicao(heroi, direcao)
  2. y, x = heroi
  3. case direcao
  4. when "W" then y -= 1
  5. when "S" then y += 1
  6. when "A" then x -= 1
  7. when "D" then x += 1
  8. end
  9. [y, x]
  10. end

您可能考虑为您的方法编写 测试

英文:

Your calcula_nova_posicao implementation alters the passed heroi array and then returns it. Therefore, your nova_posicao and heroi will be the same after the call (not only containing the same values, but both referring to the very same object).

Here's a quick example on how to verify this: (equal? checks if two objects are in fact the same object)

  1. a = [0, 0]
  2. b = calcula_nova_posicao(a, &quot;S&quot;)
  3. a #=&gt; [1, 0] &lt;- note that &quot;a&quot; has changed!
  4. b #=&gt; [1, 0]
  5. a.equal?(b) #=&gt; true

> why when I call a function passing a reference to an array, the array is changing in ruby?

This is a very fundamental behavior in Ruby. Some objects are mutable, i.e. you can alter such objects by sending certain messages to them (so-called destructive methods). Examples of mutable objects are strings, hashes and arrays. It doesn't matter how or where the (destructive) message is sent or who sends it. Once received, the object will alter itself.

Here's an example of two variables a and b referring to the same object, a string. If you send the upcase! message to the string, it will upcase its characters and both variables will reflect that change: (because they are still referring to the same object which now has changed)

  1. a = &quot;foo&quot;
  2. b = a
  3. a.upcase! # or b.upcase!
  4. a #=&gt; &quot;FOO&quot;
  5. b #=&gt; &quot;FOO&quot;

The same applies to method arguments. If you pass a mutable object into a method, the method can alter it, just like you can outside the method:

  1. def my_method(s)
  2. s.upcase!
  3. end
  4. a = &quot;foo&quot;
  5. my_method(a)
  6. a #=&gt; &quot;FOO&quot;

Back to your problem. You probably want calcula_nova_posicao to 1) return a new array and 2) leave the passed-in array unchanged. The latter is important to clear the current H from the map. But it's also a good idea in general to not change arguments within methods because it can lead to unexpected results and hard to find bugs.

Here's one implementation:

  1. def calcula_nova_posicao(heroi, direcao)
  2. case direcao
  3. when &quot;W&quot;
  4. [heroi[0] - 1, heroi[1]]
  5. when &quot;S&quot;
  6. [heroi[0] + 1, heroi[1]]
  7. when &quot;A&quot;
  8. [heroi[0], heroi[1] - 1]
  9. when &quot;D&quot;
  10. [heroi[0], heroi[1] + 1]
  11. end
  12. end

Here's another one using array decomposition:

  1. def calcula_nova_posicao(heroi, direcao)
  2. y, x = heroi
  3. case direcao
  4. when &quot;W&quot; then y -= 1
  5. when &quot;S&quot; then y += 1
  6. when &quot;A&quot; then x -= 1
  7. when &quot;D&quot; then x += 1
  8. end
  9. [y, x]
  10. end

You might consider writing tests for your methods.

huangapple
  • 本文由 发表于 2023年5月17日 08:32:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76267865.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定