【问题标题】:How to Use Hashes to Store methods in Ruby?如何在 Ruby 中使用哈希存储方法?
【发布时间】:2013-07-12 01:55:31
【问题描述】:

我想知道是否有人可以向我解释为什么我不能使用我的Hash($player_x_command_list) 来访问我的player_x_move(position) 方法?这样做的正确方法是什么?

我想要做的是,创建一个 Hash,让用户输入输入并将其反射回我在课堂上的方法。

我尝试了各种更改方法,以便代码可以正常工作,但没有任何效果。

我收到了错误:

Game:Class 的未定义方法 `player_x_move' (NoMethodError)

这是否意味着哈希不能存储方法?

这是我的代码:

#Tic Tac Toe Game
#The format is the below: where index 0 represents top left and index 8 represents bottom right
#goes 0,1,2
#     3,4,5
#     6,7,8
#"e" is for empty. "x" is for Player X moves and "o" is for Player O moves



class Game
  @@player_x_win_count = 0
  @@player_o_win_count = 0

  def initialize
    @board = Array.new(9, "e")
    @move_number = 0
  end

  def get_names
  puts "Hi Welcome to my Tic Tac Toe Game. The board looks like this:

  |TL|TM|TR|
  |ML|MM|MR|
  |BL|BM|BR|

  Each position of the Tic Tac Toe board is represented by two letters. To \"X\" or \"O\" a position, just input the two letters in CAPS like \"MM\"


  The command list is as follows:
  TL = top left
  TM = top mid
  TR = top right
  ML = mid left
  MM = mid mid
  MR = mid right
  BL = bottom left
  BM = bottom mid
  BR = bottom right
  board = to view the board
  new game = to clean the board and create a new game (note that this command should only be used when you don't want to continue on the current game. The game automatically creates a new game if a winner, loser, or draw is declared)
   "
    puts "Please Enter PlayerX's name. He/she will be using X's to mark the board."
    @player_x = gets.chomp

    puts "Please Enter PlayerO's name. He/she will be using O's to mark the board."
    @player_o = gets.chomp

    self.new_round
  end

$player_x_command_list = {"TL" => self.player_x_move(0), "TM" => self.player_x_move(1), "TR" => self.player_x_move(2), "ML" => self.player_x_move(3), "MM" => self.player_x_move(4), 
    "MR" => self.player_x_move(5), "BL" => self.player_x_move(6), "BM" => self.player_x_move(7), "BR" => self.player_x_move(8), "board" => self.board, "new game" => self.clean_board, 
    "win count" => self.win_count}

$player_o_command_list = {"TL" => self.player_o_move(0), "TM" => self.player_o_move(1), "TR" => self.player_o_move(2), "ML" => self.player_o_move(3), "MM" => self.player_o_move(4), 
    "MR" => self.player_o_move(5), "BL" => self.player_o_move(6), "BM" => self.player_o_move(7), "BR" => self.player_o_move(8), "board" => self.board, "new game" => self.clean_board, 
    "win count" => self.win_count}


  def enter_command_player_x
    puts "Please input your command, #{@player_x} aka PlayerX"
    command = gets.chomp
    $player_x_command_list[command]
  end

  def enter_command_player_o
    puts "Please input your command, #{@player_o} aka PlayerY. Type \"help\" to see a full list of commands"
    command = gets.chomp
    $player_o_command_list[command]
  end


  def new_round
    puts "So who wants to go first this round"
    went_first_this_round = gets.chomp
    if went_first_this_round == @player_x
      self.enter_command_player_x
    elsif went_first_this_round == @player_o
      self.enter_command_player_o
    else
      puts "Not a valid name. Please enter one of the player's names"
    end
  end

  def board
    print "|#{@board[0]}|#{@board[1]}|#{@board[2]}|\n|#{@board[3]}|#{@board[4]}|#{@board[5]}|\n|#{@board[6]}|#{@board[7]}|#{@board[8]}|"
  end

  def player_x_move(position)
    if @board[position] == "x" || @board[position] == "o"  
      return "That move was invalid as someone has already moved there. Please enter a valid move"
    end
    @board[position] = "x"
    @move_number += 1
    puts "That was move number #{@move_number} and the current board looks like: " 
    self.board
    self.check_game
    puts "Now it is #{player_o}'s turn. #{player_o} please input your next command."
    self.enter_command_player_o
  end

  def player_o_move(position)
    if @board[position] == "x" || @board[position] == "o"     
      return "That move was invalid as someone has already moved there. Please enter a valid move"
    end
    @board[position] = "o"
    @move_number += 1
    puts "That was move number #{@move_number} and the current board looks like: "
    self.board
    self.check_game
    puts "Now it is #{player_x}'s turn. #{player_x} please input your next command"
    self.enter_command_player_x
  end

  def check_game
    triple_x = "xxx"
    triple_o = "ooo"
    if @move_number == 9
      @move_number = 0
      self.clean_board
      return "The board is completely filled up. Looks like this is a draw. This is Game Over. Make a new game by setting any variable = to new.Game and using that variable to play"
    elsif @board[0] + @board[1] + @board[2] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    elsif @board[3] + @board[4] + @board[5] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    elsif @board[6] + @board[7] + @board[8] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    elsif @board[0] + @board[3] + @board[6] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    elsif @board[1] + @board[4] + @board[7] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    elsif @board[2] + @board[5] + @board[8] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    elsif @board[0] + @board[4] + @board[8] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    elsif @board[2] + @board[4] + @board[6] == triple_x
      @@player_x_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player X Wins"
    #now check if Player O Wins
    elsif @board[0] + @board[1] + @board[2] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins"
    elsif @board[3] + @board[4] + @board[5] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins"   
    elsif @board[6] + @board[7] + @board[8] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins"   
    elsif @board[0] + @board[3] + @board[6] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins"    
    elsif @board[1] + @board[4] + @board[7] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins"   
    elsif @board[2] + @board[5] + @board[8] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins"  
    elsif @board[0] + @board[4] + @board[8] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins" 
    elsif @board[2] + @board[4] + @board[6] == triple_o
      @@player_y_win_count += 1
      @move_number = 0
      self.clean_board
      return "Player O Wins"   
    else
      return "no one has WON YET! Continue your GAME!!"
    end
  end

  def clean_board
    @board = Array.new(9, "e")
  end

  def win_count
    puts "So far Player X has won #{@@player_x_win_count} times and Player O has won #{@@player_o_win_count} times."
  end

end

a = Game.new
a.get_names

【问题讨论】:

  • 您应该尽可能避免使用全局变量,而是将它们封装到一个对象中。
  • 谢谢你们到目前为止的帮助。如果有人有时间,只是最后一个简单的问题?在我的代码中,我意识到很多“.self”是可选的。我的代码将其全部取出是否更好/更有效?是否有任何实际需要的“.self”?
  • 除非需要,否则避免使用 self 限定方法调用。尤其如此,因为不能使用符合条件的 self 调用私有方法。通常需要self 的唯一情况是存在与被调用方法同名的局部变量。

标签: ruby class methods hash


【解决方案1】:

您这里有一些问题:

  1. 您的范围有误。定义哈希时,self 是类,而不是您要操作的类的实例。
  2. 当您创建散列时,散列的值将是 player_x_move 等方法在定义时的返回值

您可以使用case 大大简化此操作。

def enter_command
  puts "Please input your command, #{@player_x} aka PlayerX"
  command = gets.chomp
  case command
  when "TL"
    player_x_move(0)
  when "TM"
    player_x_move(1)
  # etc
  else
    puts "#{command} is not a valid command."
  end
end

您还可以通过创建接受enter_commandplayer_move 等参数的方法来进一步简化代码,然后将播放器传递给它们以对其进行操作。这使您不必为每个玩家重复每个方法。

您可以考虑的其他事情只是根据给定的命令查找移动的索引:

COMMAND_POSITIONS = %w(TL TM TR ML MM MR BL BM BR)
def enter_command(player)
  puts "Please input your command, #{player}"
  command = gets.chomp
  case command
  when *COMMAND_POSITIONS
    player_move player, COMMAND_POSITIONS.index(command)
  when "board"
    board
  when "new game"
    clean_board
  when "win count"
    win_count
  else
    puts "#{command}" is not a valid command
  end
end

【讨论】:

    【解决方案2】:

    哈希不能存储方法。方法不是对象,但可以转换为 Procs。方法定义返回 nilchanged since this writing。但是,您可以存储 Proc 或 lambda。您还可以存储方法的返回(评估)。

    这是一个示例,说明如何将 Proc 存储到从方法派生的哈希中。

    >> def hello(name)
    >>   "Hello #{name}!"
    >> end
    => nil
    >> my_stored_methods = {:hello => method(:hello).to_proc} 
    => {:hello=>#<Proc:0x816f604 (lambda)>}
    >> my_stored_methods[:hello].call("World")
    => "Hello World!"
    

    不要让我调用散列“my_stored_methods”的 stroop 效应让你相信那里实际上存储了一个真实的方法。它是存储在散列中的 lambda(一个专门的 Proc),并根据需要使用。事实上,如果我没有在那里使用 .to_proc,它会保存一个 Method 实例。

    此外,这个解决方案不会随着 open 方法的发展而增长,如果方法发生变化,存储在哈希中的 Proc 将继续像方法在存储为 Proc 时那样工作.

    正如@AndrewMarshall 提醒我的那样,我本可以将其保留为 Method 实例。这仍然不会“存储”方法本身,因为当方法更改时,结果仍将是源方法的历史行为,就像存储时一样。它还提供了更强的“Stroop 效应”,因为您可能会错误地认为实际方法存储在那里。根本不是。


    【讨论】:

    • -1:调用"foo".method(:length)会返回Method class的一个实例,所以在这个意义上方法是对象。
    • 当然,但是给我方法的对象 ID。
    • stackoverflow.com/a/2602485/485864 它们不是对象,就像任何其他对象一样。但可以做到。
    • @vgoff 您从说“哈希不能存储方法”开始,并且由于 Hash 可以包含 Method 实例我不同意,但我想在技术上方法和相应的方法之间存在区别Method 实例,所以我们可能都是对的(我会删除 -1,但如果不修改答案,我不会改变主意)。
    • “[不要]相信那里实际上存储了一个真实的方法。它是存储在哈希中的 lambda(一个专门的 Proc)”,嗯,是的,但只是因为你在上面调用了to_proc。如果你没有这样做,一个 Method 对象会被存储在那里就好了。
    猜你喜欢
    • 2013-05-10
    • 2017-07-31
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多