![](/img/trans.png)
[英]How to refactor this Ruby sanitize hash method to make it more idiomatic?
[英]how to make my first Ruby effort more idiomatic
為我的新工作挑選Ruby是有幫助的,所以今天早上我寫了以下內容。 它需要一個我玩過的國際象棋PGN文件,並通過第一步將它們編入索引。 我希望就如何使其更加“慣用”提出任何建議。
由於它不接受命令行參數(例如文件名),並且也不是面向對象的建議,因此絕對歡迎這些建議。
請記住,我正在為所有游戲的所有動作(不僅是第一步)創建索引,因為我希望最終能索引的不僅僅是第一步。
數據遵循代碼。
games = []
file = File.new("jemptymethod.pgn", "r")
is_header = false
is_score = false
Game = Struct::new(:header, :score)
while (line = file.gets)
if !line.chomp.empty?
if !is_score && !is_header
game = Game::new('','')
end
if /^\[/.match(line)
is_header = true
game.header << line
else
is_score = true
game.score << line
end
else
if is_score
is_score = false
is_header = false
games << game
end
end
end
file.close
puts "# Games: " + games.length.to_s
moves_index = {}
first_moves = {}
games.each { |gm|
#the following output should essentially be lossless
#with the possible exception of beginning or ending newlines
#
#puts gm.header + "\n"
#puts gm.score + "\n"
score_tokens = gm.score.split(/\s+/);
game_moves = []
score_tokens.each_index{|i|
if i%3 != 0
move_token = score_tokens[i]
if !moves_index.has_key?(move_token)
moves_index[move_token] = moves_index.keys.length
end
game_moves << moves_index[move_token]
end
}
first_move = moves_index.index(game_moves[0])
if !first_moves.has_key?(first_move)
first_moves[first_move] = 1
else
first_moves[first_move] = 1 + first_moves[first_move]
end
}
# sorting hashes by value: http://nhw.pl/wp/2007/06/11/sorting-hash-by-values
first_moves.sort{|a,b| -1*(a[1]<=>b[1])}.each{|k,v|
puts "1. #{k} occurred #{v} times"
}
數據(僅3個游戲,我已經使用了25個):
[Event "Enough With the Draws Already ;)"]
[Site "http://www.queenalice.com/game.php?id=533406"]
[Date "2009.2.1"]
[Round "-"]
[White "Troy"]
[Black "jemptymethod"]
[Result "1/2-1/2"]
[WhiteElo "1300"]
[BlackElo "2076"]
[ECO "C36"]
1. e4 e5 2. f4 exf4 3. Nf3 Be7 4. Bc4 Nf6 5. Qe2 d5 6. exd5 Nxd5 7. O-O Be6 8.
d4 Nc6 9. Nc3 O-O 10. Nxd5 Bxd5 11. Bxd5 Qxd5 12. Bxf4 Bd6 13. Qd2 Rae8 14. Bxd6
Qxd6 15. Rae1 h6 16. c3 Qd5 17. b3 Qa5 18. h3 a6 19. Rf2 Re7 20. Rxe7 Nxe7 21.
Ne5 Nd5 22. c4 Qxd2 1/2-1/2
[Event "AUTO-MASTER-620"]
[Site "http://www.queenalice.com/game.php?id=545265"]
[Date "2009.2.23"]
[Round "2"]
[White "testouverture"]
[Black "jemptymethod"]
[Result "1/2-1/2"]
[WhiteElo "2240"]
[BlackElo "2179"]
[ECO "A52"]
1. d4 Nf6 2. c4 e5 3. dxe5 Ng4 4. Nf3 Bc5 5. e3 Nc6 6. Be2 O-O 7. O-O Re8 8. b3
Ngxe5 9. Bb2 Nxf3+ 10. Bxf3 Ne5 11. Nc3 a5 12. Ne4 Bf8 13. Bh5 Ra6 14. f4 Ng6
15. Ng5 d5 16. Nxf7 Kxf7 17. f5 Kg8 18. fxg6 hxg6 19. Qd4 Qe7 20. Bf3 dxc4 21.
Qxc4+ Be6 22. Qc3 c6 23. Be2 Raa8 24. Bd3 Bf5 25. Bxf5 gxf5 26. Rf3 Qc5 27. Re1
Qxc3 28. Bxc3 g6 29. g4 Bg7 30. Bxg7 fxg4 31. Rg3 Kxg7 32. Rxg4 Rad8 33. Kf2
1/2-1/2
[Event "AUTO-MASTER-620"]
[Site "http://www.queenalice.com/game.php?id=545266"]
[Date "2009.2.23"]
[Round "2"]
[White "jemptymethod"]
[Black "testouverture"]
[Result "0-1"]
[WhiteElo "2079"]
[BlackElo "2306"]
[ECO "B22"]
1. e4 c5 2. c3 d5 3. exd5 Qxd5 4. d4 Nc6 5. dxc5 Qxd1+ 6. Kxd1 e5 7. Be3 Nf6 8.
b4 a5 9. b5 Ne7 10. Nf3 Ng4 11. Bc4 Nf5 12. Ke2 Nfxe3 13. fxe3 Bxc5 14. h3 Nxe3
15. Nxe5 f6 0-1
這是我如何執行此操作的快速解決方案。 這里可能有很多需要消化的地方,所以隨時提出問題,但是閱讀Ruby Array或Enumerable文檔應該可以回答大多數關於我所做的事情的知識,並且有很多關於ruby類的優秀教程。 這是一個很好的理解我在這里的類中使用的訪問器而不是struct的訪問器。
class Game
attr_accessor :header, :moves
def initialize
self.header = []
end
end
games = []
game = Game.new
File.open('jemptymethod.pgn').each_line do |line|
next if line.chomp.empty?
if game.moves
games << game
game = Game.new
end
if /^\[/.match(line)
game.header << line
else
moves = line.split(/\d+\.\s*/) # splitting on the move numbers so that we don't have to iterate through to remove them
moves.shift # getting rid of first empty move since the split on '1. ' created an array element before the '1. '
game.moves = moves
end
end
games << game # add last game since the first part of the file loop doesn't execute again to do it
puts "# Games: " + games.length.to_s
first_moves = games.map {|game| game.moves[0]} # Could easily iterate over the size of the longest game to get other moves (eg second move, etc)
first_moves_count = first_moves.inject(Hash.new(0)) {|h, move| h[move] += 1; h} # Read ruby documentation on inject to see how this works
first_moves_count.each do |move, count|
puts "1. #{move} occurred #{count} times"
end
我還沒有進行完整的重構,因為我想保持完整的原始代碼,以免混淆不清。 主要變化是引入了用於處理解析的Game
類。 此類的實現可以進行很多改進,但是可以在無需過多更改代碼的情況下運行。 另外,還有一些小問題:
代替File.new
,使用File.open
讀取文件,並File.open
提供一個使用file
參數的塊。 文件在塊末自動關閉。
使用a += 1
代替a = a + 1
。
我用一個簡單的表示法編寫了一個解析器,用於處理網球比賽的逐場細節 。 您可能需要查看該代碼,以獲取解析游戲動作的示例。 它實際上與您正在執行的操作非常相似。 大部分代碼在/lib
目錄中。 解析邏輯位於parser.rb
,游戲組件位於其他文件中。 我建議您通過添加Move
類,以類似的方式破壞象棋游戲。
無論如何,這是我對代碼的一半重構:
class Game
attr_accessor :header, :score, :moves
def initialize
@header = ""
@score = ""
@moves = []
end
def first_move
moves_index.index(moves[0])
end
def moves_index
moves_index = {}
score.split(/\s+/).each_with_index do |move,i|
if i%3 != 0
unless moves_index.has_key?(move)
moves_index[move] = moves_index.keys.length
end
moves << moves_index[move]
end
end
moves_index
end
end
games = []
is_header = false
is_score = false
File.open("jemptymethod.pgn") do |file|
while (line = file.gets)
if !line.chomp.empty?
if !is_score && !is_header
game = Game.new
end
if line[0,1] == '['
is_header = true
game.header << line
else
is_score = true
game.score << line
end
elsif is_score
is_score = false
is_header = false
games << game
end
end
end
puts "# Games: " + games.length.to_s
first_moves = {}
#the following output should essentially be lossless
#with the possible exception of beginning or ending newlines
#
#puts gm.header + "\n"
#puts gm.score + "\n"
games.each do |gm|
if !first_moves.has_key?(gm.first_move)
first_moves[gm.first_move] = 1
else
first_moves[gm.first_move] += 1
end
end
# sorting hashes by value: http://nhw.pl/wp/2007/06/11/sorting-hash-by-values
first_moves.sort{|a,b| -1*(a[1]<=>b[1])}.each{|k,v|
puts "1. #{k} occurred #{v} times"
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.