为什么当我调用一个函数传递一个引用到一个数组时,数组在ruby中发生了变化?[关闭]

igsr9ssn  于 2023-05-22  发布在  Ruby
关注(0)|答案(1)|浏览(122)

**关闭。**此题需要debugging details。目前不接受答复。

编辑问题以包含desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem。这将帮助其他人回答这个问题。
4天前关闭。
截至4天前,社区正在审查是否重新开放此问题。
Improve this question
我正在学习用Ruby创建一个小游戏的教程,突然遇到了一个我无法理解的bug。当我仔细查看时,我意识到错误发生在fogefoge.rb的第51行,调用nova_posicao = calcula_nova_posicao heroi, direcao
在这一点上的教程,英雄“H”应该能够移动与关键“ASDW”。我注意到第49行heroi = encontra_jogador mapa中的数组heroi被更改了,这在应用程序中引入了bug。之后,我创建了另一个名为heroi2的变量,代码工作正常。
为什么当我调用fogefoge.rb的第51行时,数组正在改变?
从我在Rubydocs上阅读到的内容来看,我只是不明白为什么会发生这种情况。

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

我原以为“H”会在空白处移动,但“H”却向几个方向重复
输出:

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

如果你取消注解第50行的第二个引用,然后将第51行改为nova_posicao = calcula_nova_posicao heroi2, direcao,那么代码就可以工作了。

ih99xse1

ih99xse11#

您的calcula_nova_posicao实现更改了传递的heroi数组,然后返回它。因此,nova_posicaoheroi在调用后将是相同的(不仅包含相同的值,而且都引用相同的对象)。
这里有一个关于如何验证这一点的快速示例:(equal?检查两个对象是否是同一个对象)

a = [0, 0]
b = calcula_nova_posicao(a, "S")

a #=> [1, 0]  <- note that "a" has changed!
b #=> [1, 0]

a.equal?(b) #=> true

为什么当我调用一个函数传递一个引用到一个数组时,数组在ruby中发生了变化?
这是Ruby中非常基本的行为。有些对象是可变的,即您可以通过向这些对象发送某些消息(所谓的 * 破坏性方法 *)来更改它们。可变对象的例子有字符串、散列和数组。不管(破坏性)信息是如何或在哪里发送的,也不管是谁发送的。一旦接收到,对象将改变自身。
下面是两个变量ab引用同一个对象的例子,一个字符串。如果你向字符串发送upcase!消息,它将大写字符,两个变量都将反映这种变化:(因为它们仍然引用现在已经改变的相同对象)

a = "foo"
b = a

a.upcase!   # or b.upcase!

a #=> "FOO"
b #=> "FOO"

这同样适用于方法参数。如果你将一个可变对象传入一个方法,该方法就可以改变它,就像你在该方法之外所做的一样:

def my_method(s)
  s.upcase!
end

a = "foo"
my_method(a)

a #=> "FOO"

回到你的问题。您可能希望calcula_nova_posicao 1)返回一个 new 数组,2)保持传入的数组不变。后者对于从Map中清除当前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

你可以考虑为你的方法编写测试。

相关问题