[Ruby] Blocks -- Ex
Iterating with Blocks
Let's build a Library class that will manage our growing collection of games. We've already written a list
method that prints the names of all our games, but it uses an ugly for
loop to iterate the list. Refactor it to use each
with a block instead.
#seed.rb GAMES = [ Game.new('Contra', year: 1987, system: 'NES'), Game.new('Civilization', year: 1991, system: 'PC'), Game.new('The Legend of Zelda', year: 1986, system: 'NES'), Game.new('Mega Man X2', year: 1995, system: 'SNES'), Game.new('Super Metroid', year: 1994, system: 'SNES'), Game.new('Sim City 2000', year: 1993, system: 'PC'), Game.new('Starcraft', year: 1998, system: 'PC') ]
#game.rb class Game attr_accessor :name, :year, :system attr_reader :created_at def initialize(name, options={}) self.name = name self.year = options[:year] self.system = options[:system] @created_at = Time.now end end
class Library attr_accessor :games def initialize(games = []) self.games = games end def list for i in 0...(games.length) game = games[i] puts game.name end end end
Answer:
class Library attr_accessor :games def initialize(games = []) self.games = games end def list games.each do |game| puts game.name end end end
Yielding to Blocks
We'd like to be able to operate on our games by system. Implement an each_on_system
method that iterates over our games
using each
and yields to a block for every game on the requested system. To test that it's working, we'll call each_on_system
with a simple block that prints a message for every Super Nintendo game in our library. See the example.rb
below.
library = Library.new(GAMES) library.each_on_system("SNES") { puts "Found a Super Nintendo game" }
Answer:
class Library attr_accessor :games def initialize(games = []) self.games = games end def each_on_system(system) self.games.each do |game| yield if game.system == system end end end
Passing Arguments to Blocks
Our each_on_system
method is working, but it's not very useful unless the block has access to each game that we find. Modify each_on_system
to pass the Game object into the block so we can print its name.
class Library attr_accessor :games def initialize(games = []) self.games = games end def each_on_system(system) games.each { |game| yield game if game.system == system} end end
Returning Values from Blocks
Earlier we wrote a list
method that prints the name of each game in our library. We can make the output formatting more flexible by allowing a block to be passed to the list
method. We'll yield each game to the block and allow the block to format and return a string for us to display. Modify the list
method to yield to a block and print whatever the block returns.
library = Library.new(GAMES) library.list { |game| "#{game.name} (#{game.system}) - #{game.year}" }
Answer:
class Library attr_accessor :games def initialize(games = []) self.games = games end def list games.each do |game| puts yield game end end end
Using Enumerable
Let's add the power of Ruby's Enumerable module to our game library. Implement an each
method that yields each game in the library. Finally, include the Enumerable module so that we'll be able to call methods like select
and collect
on our library.
library = Library.new(GAMES) library.select { |game| game.year == 1986 } library.collect { |game| game.system }
Answer:
class Library include Enumerable attr_accessor :games def initialize(games = []) self.games = games end def each self.games.each {|game| yield game} end end
Refactoring with Blocks
Now that our library is complete, let's play some games! A friend has given us his Emulator
class to use, and we've implemented methods to play a game and grab a screenshot. But look at all that duplicated code in play
and screenshot
. Refactor the duplication (the begin
, new
and rescue
parts) into a private method called emulate
that handles the emulator setup and exception handling and yields the emulator instance to a block.
class Emulator def initialize(system) # Creates an emulator for the given system end def play(game) # Runs the given game in the emulator end def start(game) # Loads the given game but doesn't run it end def screenshot # Returns a screenshot of the currently loaded game end end
class Game attr_accessor :name, :year, :system attr_reader :created_at def initialize(name, options={}) self.name = name self.year = options[:year] self.system = options[:system] @created_at = Time.now end def play begin emulator = Emulator.new(system) emulator.play(self) rescue Exception => e puts "Emulator failed: #{e}" end end def screenshot begin emulator = Emulator.new(system) emulator.start(self) emulator.screenshot rescue Exception => e puts "Emulator failed: #{e}" end end end
Answer:
class Game attr_accessor :name, :year, :system attr_reader :created_at def initialize(name, options={}) self.name = name self.year = options[:year] self.system = options[:system] @created_at = Time.now end def play emulate do |emulator| emulator.play(self) end end def screenshot emulate do |emulator| emulator.start(self) emulator.screenshot end end private def emulate emulator = Emulator.new(system) yield emulator rescue Exception => e puts "Emulator failed: #{e}" end end
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具