Ruby 数组分配在不应该更新时更新

Ruby Array assignments updating when its not supposed to

基本上我正在制作一个可以为我解决数独谜题的程序,我想保留原始输入矩阵的起点,以便必须选择重置棋盘并重新开始,如果程序得到卡在了兔子洞里。我想将我的原件存储在一个名为@start 的字段变量中,并且在算法过程中永远不会触及它。

是否有某种我必须使用的关键字?我试过 .map,我试过从输入矩阵手动复制它。

class Game
def initialize(gamematrix)
  @start=gamematrix
  @rows=gamematrix
    puts "rows - #{@rows}"
  @cols=@rows.transpose
end

def findnils
puts "finding the nils"
sleep 0.1

     nilspaces=[]
    g=0
    while g<@cols.length
puts "3  we parse through col #{g}"
sleep 0.1
      p=0
        while p<@rows.length
if @rows[g][p]!=nil
  puts "4 [#{g}][#{p}]"
end
sleep 0.1
          if @rows[g][p]==nil
           puts "      [#{g}][#{p}] :found a nil!!!"
              nilspaces<<[g,p]
          end
        p+=1
        end
      g+=1
    end
    return nilspaces

end

def sudku
puts '1  we\'re in the sudku method'
#sleep 0.8
    #update the coordinatebank of nill spaces in the matric
#sleep 0.8
 nilspaces=findnils

  while !win?
  puts '5 now our nilspaces array now looks like this: '
  puts "nils: [#{nilspaces}]"
  sleep 0.4

      #randomly find an empty nil space, find its coordinates in the matrix
    current=nilspaces.sample
  puts "out current nilspace is [#{current}]"
  sleep 0.2
      nilspaces.delete_if{|e| e==current}
  puts "nils: [#{nilspaces}]"
  sleep 0.2
      #then use the guessNum method to guess based on the coordinates
      guessedN=guessNum(@cols[current[0]], @rows[current[1]])
  puts "guessed: #{guessedN}"
  sleep 0.2
      #assign out guessed number to replace the nil
      if guessedN != 999
        @rows[current[0]][current[1]]=guessedN
  sleep 0.1
  puts "guessed rturns a number so @rows is #{@rows[current[0]][current[1]]}"
  puts "          start matrix: #{@start}"

      else
        puts "=========================="
        #reset
          puts "resets everything"
            sleep 0.3
            @rows=@start
            @cols=@rows.transpose
          puts "rows: #{@rows}"
          puts "cols: #{@cols}"
        nilspaces=findnils
        puts "=========================="
        sleep 0.1
      end

      display
    sleep 0.4

  end

end



def guessNum(vset, hset)
                          #create an array of numbers to choose from
choice=(1..@rows.length).to_a
                          #subtract everything in the choice array thats in the hset
choice.delete_if {|e| hset.include?(e)}
                          #subtract everything in the choice array thats in the vset
choice.delete_if {|e| vset.include?(e)}
                          #if nothing is left in the choice array return a string called empty
                          #otherwise return a random number from the array (the choice array)
    if choice.empty?
      return 999
    else
      return choice.sample
    end

end

def display
  puts "--------------------"
  for g in @rows
    out="        "
    for l in g
      out+= "#{'%03s' % l.to_s}"
    end
    out+=" \n"
    puts out
  end
  puts "--------------------"
end


def win?
[@rows.all? { |e| e.uniq.length==@rows.length }, @cols.all? { |e| e.uniq.length==@cols.length }].all? { |e| e==true  }
end

end

驱动测试是这样的:

easygame4x4=[[4,nil,nil,nil],[nil,3,4,nil],[nil,1,2,nil],[nil,nil,nil,1]]

gameone=Game.new(easygame4x4)

gameone.sudku

你可以 运行 这个看看它是如何工作的,我在里面放了一堆 puts 语句和 sleep 语句来引导任何人完成这个过程,问题是,我的 @start 矩阵总是得到更新@rows 矩阵

使用Ruby方法clone, freeze, and dup

示例:

def initialize(gamematrix)
  @start=gamematrix.clone.freeze

当你anything = @start这样做时:

@rows=@start.dup

您在代码中看到的是 Ruby 修改对象:

array1 = ["a"]
array2 = array1  # now these variables point to the same place
array2[0] #=> "a" as you expect
array1[0] = "b"
array2[0] #=> "b" because array1 and array2 point to the same place

clone 做的是浅拷贝,a.k.a。浅克隆:

array1 = ["a"]
array2 = array1.clone # now array2 points to a new place
array2[0] #=> "a" as you expect
array1[0] = "b"
array2 #=> ["a"] because array2 points to its own place, not array1's place

浅拷贝和深拷贝的区别:

array1 = [["a"]]
array2 = array1.clone # array2 points to a new place, which in turn points to the array that contains "a"
array1[0][0] = "b"  # you're changing the array that contains "a"
array2 #=> [["b"]]  # because array2 points to its own place, which in turn still points to the same array that contained "a" 

您的代码需要的是深拷贝,a.k.a。深度克隆。 Ruby 没有此方法(恕我直言,这是一个错误),因此典型的解决方案是使用 gem 或 Ruby Marshall 方法。看 Why isn't there a deep copy method in Ruby?

dup 的作用类似于克隆,只是 dup 不复制冻结状态:

s = "foo"
s.clone.frozen?  # true
s.dup.frozen?  # false

您的特定应用程序正在修改行,因此您的重置功能应使用 dup 而不是 clone

如果您正在为矩阵编写自己的对象 class,那么您可能需要编写自己的 clone 方法,例如:

class GameMatrix

  def clone
    # create a clone as you want,
    # such as initializing a new object,
    # and/or cloning sub-objects, etc.,
    # including singleton/class methods,
    # including frozen state, etc.
  end

  def dup
    # create a duplicate as you want,
    # such as initializing a new object,
    # and/or duplicating sub-objects, but
    # excluding singleton/class methods,
    # excluding frozen state, etc.
  end

end

附加说明...

clonedup 方法由每个 class 来定义,因此请阅读每个方法的文档。通常,clone 是 dup 的超集,因为 clone 还复制单例方法、冻结状态等。

freeze 方法并不是真正必要的,它的行为出乎意料(恕我直言),但它仍然值得使用该方法,因为它有一点帮助,并向您的其他代码阅读器表明您希望该对象成为常数。

我弄明白了:它是一个矩阵输入,所以我必须将它 .map 两次才能真正克隆它。