英文:
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.
File "main.rb":
require_relative "fogefoge"
inicia_fogefoge
File "mapa1.txt":
XXXXX
X H X
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
File "ui.rb":
def da_boas_vindas
puts "bem vindo ao Foge-foge"
puts "Qual é o seu nome?"
nome = gets.strip
puts "\n\n\n\n\n"
puts "Começaremos o jogo para você, #{nome}"
nome
end
def desenha(mapa)
puts mapa
end
def pede_movimento
puts "Para onde deseja ir?"
movimento = gets.strip
end
#File "fogefoge.rb":
require_relative "ui"
def le_mapa(numero)
arquivo = "mapa#{numero}.txt"
texto = File.read(arquivo)
mapa = texto.split "\n"
# p mapa
end
def encontra_jogador(mapa)
caractere_do_heroi = "H"
mapa.each_with_index do |linha_atual, linha|
coluna_do_heroi = linha_atual.index caractere_do_heroi
if coluna_do_heroi
return [linha, coluna_do_heroi]
end
end
end
def calcula_nova_posicao(heroi, direcao)
case direcao
when "W"
heroi[0] -= 1
when "S"
heroi[0] += 1
when "A"
heroi[1] -= 1
when "D"
heroi[1] += 1
end
heroi
end
def posicao_valida?(mapa, posicao)
linhas = mapa.size
colunas = mapa[0].size
estourou_linhas = posicao[0] < 0 || posicao[0] >= linhas
estourou_colunas = posicao[1] < 0 || posicao[1] >= colunas
if estourou_linhas || estourou_colunas
return false
end
if mapa[posicao[0]][posicao[1]] == "X"
return false
end
true
end
def joga(nome)
mapa = le_mapa 1
while true
desenha mapa
direcao = pede_movimento
heroi = encontra_jogador mapa
#heroi2 = encontra_jogador mapa
nova_posicao = calcula_nova_posicao heroi, direcao #At this call
if !posicao_valida? mapa, nova_posicao
next
end
mapa[heroi[0]][heroi[1]] = " "
mapa[nova_posicao[0]][nova_posicao[1]] = "H"
end
end
def inicia_fogefoge
nome = da_boas_vindas
joga nome
end
I was expecting that the "H" would move around through the blank spaces, but the "H" was repeated to a few directions.
OUTPUT:
bem vindo ao Foge-foge
Qual é o seu nome?
marcos
Começaremos o jogo para você, marcos
XXXXX
X H X
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
Para onde deseja ir?
d
XXXXX
X H X
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
Para onde deseja ir?
D
XXXXX
X HHX
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
Para onde deseja ir?
S
XXXXX
X HHX
X X X
X X X
X X
X
XXX
X
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.
File "main.rb"
require_relative "fogefoge"
inicia_fogefoge
File "mapa1.txt"
XXXXX
X H X
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
File "ui.rb"
def da_boas_vindas
puts "bem vindo ao Foge-foge"
puts "Qual é o seu nome?"
nome = gets.strip
puts "\n\n\n\n\n"
puts "Começaremos o jogo para você, #{nome}"
nome
end
def desenha(mapa)
puts mapa
end
def pede_movimento
puts "Para onde deseja ir?"
movimento = gets.strip
end
#File "fogefoge.rb"
require_relative "ui"
def le_mapa(numero)
arquivo = "mapa#{numero}.txt"
texto = File.read(arquivo)
mapa = texto.split "\n"
# p mapa
end
def encontra_jogador(mapa)
caractere_do_heroi = "H"
mapa.each_with_index do |linha_atual, linha|
coluna_do_heroi = linha_atual.index caractere_do_heroi
if coluna_do_heroi
return [linha, coluna_do_heroi]
end
end
end
def calcula_nova_posicao(heroi, direcao)
case direcao
when "W"
heroi[0] -= 1
when "S"
heroi[0] += 1
when "A"
heroi[1] -= 1
when "D"
heroi[1] += 1
end
heroi
end
def posicao_valida?(mapa, posicao)
linhas = mapa.size
colunas = mapa[0].size
estourou_linhas = posicao[0] < 0 || posicao[0] >= linhas
estourou_colunas = posicao[1] < 0 || posicao[1] >= colunas
if estourou_linhas || estourou_colunas
return false
end
if mapa[posicao[0]][posicao[1]] == "X"
return false
end
true
end
def joga(nome)
mapa = le_mapa 1
while true
desenha mapa
direcao = pede_movimento
heroi = encontra_jogador mapa
#heroi2 = encontra_jogador mapa
nova_posicao = calcula_nova_posicao heroi, direcao #At this call
if !posicao_valida? mapa, nova_posicao
next
end
mapa[heroi[0]][heroi[1]] = " "
mapa[nova_posicao[0]][nova_posicao[1]] = "H"
end
end
def inicia_fogefoge
nome = da_boas_vindas
joga nome
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>
bem vindo ao Foge-foge
Qual é o seu nome?
marcos
Começaremos o jogo para você, marcos
XXXXX
X H X
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
Para onde deseja ir?
d
XXXXX
X H X
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
Para onde deseja ir?
D
XXXXX
X HHX
X X X
X X X
X X
X
XXX
X
X F X
XXXXX
Para onde deseja ir?
S
XXXXX
X HHX
X X X
X X X
X X
X
XXX
X
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_posicao
和 heroi
将相同(不仅包含相同的值,而且都引用同一个对象)。
以下是一个快速示例,说明如何验证这一点:(equal?
用于检查两个对象是否实际上是同一个对象)
a = [0, 0]
b = calcula_nova_posicao(a, "S")
a #=> [1, 0] <- 请注意 "a" 已经改变!
b #=> [1, 0]
a.equal?(b) #=> true
为什么我在 Ruby 中调用一个传递数组引用的函数时,数组会发生更改?
这是 Ruby 中的一个非常基本的行为。某些对象是 可变的,也就是说,您可以通过向它们发送某些消息(所谓的 破坏性方法)来更改这些对象。可变对象的示例包括字符串、哈希和数组。不管消息是如何发送或由谁发送的,一旦接收到,对象就会自行更改。
以下是两个变量 a
和 b
的示例,它们引用同一个字符串对象。如果向字符串发送 upcase!
消息,它将大写其字符,两个变量都将反映这种更改:(因为它们仍然引用相同已更改的对象)
a = "foo"
b = a
a.upcase! # 或 b.upcase!
a #=> "FOO"
b #=> "FOO"
同样适用于方法参数。如果将可变对象传递给方法,方法可以像在方法外部一样更改它:
def my_method(s)
s.upcase!
end
a = "foo"
my_method(a)
a #=> "FOO"
回到您的问题。您可能希望 calcula_nova_posicao
1) 返回一个 新 数组,2) 不更改传入的数组。后者对于清除地图上当前的 H
非常重要。但一般来说,不在方法内更改参数是一个好主意,因为它可能导致意外的结果和难以找到的错误。
以下是一个实现:
def calcula_nova_posicao(heroi, direcao)
case direcao
when "W"
[heroi[0] - 1, heroi[1]]
when "S"
[heroi[0] + 1, heroi[1]]
when "A"
[heroi[0], heroi[1] - 1]
when "D"
[heroi[0], heroi[1] + 1]
end
end
以下是另一种使用 数组分解 的方法:
def calcula_nova_posicao(heroi, direcao)
y, x = heroi
case direcao
when "W" then y -= 1
when "S" then y += 1
when "A" then x -= 1
when "D" then x += 1
end
[y, x]
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)
a = [0, 0]
b = calcula_nova_posicao(a, "S")
a #=> [1, 0] <- note that "a" has changed!
b #=> [1, 0]
a.equal?(b) #=> 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)
a = "foo"
b = a
a.upcase! # or b.upcase!
a #=> "FOO"
b #=> "FOO"
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:
def my_method(s)
s.upcase!
end
a = "foo"
my_method(a)
a #=> "FOO"
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:
def calcula_nova_posicao(heroi, direcao)
case direcao
when "W"
[heroi[0] - 1, heroi[1]]
when "S"
[heroi[0] + 1, heroi[1]]
when "A"
[heroi[0], heroi[1] - 1]
when "D"
[heroi[0], heroi[1] + 1]
end
end
Here's another one using array decomposition:
def calcula_nova_posicao(heroi, direcao)
y, x = heroi
case direcao
when "W" then y -= 1
when "S" then y += 1
when "A" then x -= 1
when "D" then x += 1
end
[y, x]
end
You might consider writing tests for your methods.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论