|
From: Matthew Moss on 4 Jul 2008 12:57 [Note: parts of this message were removed to make it a legal post.] Apologies if this appears as a repost, but the mailing list complained it was too large. Posting now in two parts: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- The three rules of Ruby Quiz 2: 1. Please do not post any solutions or spoiler discussion for this quiz until 48 hours have passed from the time on this message. 2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A permanent, new website is in the works for Ruby Quiz 2. Until then, please visit the temporary website at <http://splatbang.com/rubyquiz/>. 3. Enjoy! Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone on Ruby Talk follow the discussion. Please reply to the original quiz message, if you can. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- This week's quiz might look a little scary, but it's not as bad as it seems. Give it a shot, and ask for help if you get stuck! ## Statistician II Last week's quiz started the creation of a line-based pattern-matching system: our statistician. This week, your task is to further develop a solution from last week: organize the code and provide a more interesting interface. The first thing is organization. This little library should be reusable and not tied to any particular parsing need. So we want to separate out the "Statistician" from the client. To do this means moving the appropriate code into a separate file called `statistician.rb`, containing: # statistician.rb module Statistician # This module is your task! Your code goes here... end Meanwhile, the client code will now begin with: # client.rb require 'statistician' Simple, eh? Next, we will move the rules from their own data file and bring them into the code. Admittedly, moving data into code usually is not a wise thing to do, but as the primary data is that which the rules parse, we're going to do it anyway. Besides, this is Ruby Quiz, so why not? Simultaneously, we're going to group rules together: rules that while may differ somewhat in appearance, essentially represent the same kind or category of data. As the rules and category are client data, they will go into the client's code. Here's an example to begin, borrowing the LotRO rules used last week. # client.rb class Offense < Statistician::Reportable rule "You wound[ the] <name>[ with <attack>] for <amount> point[s] of <kind>[ damage]." rule "You reflect <amount> point[s] of <kind> damage to[ the] <name>." end class Victory < Statistician::Reportable rule "Your mighty blow defeated[ the] <name>." end Next, we need a parser (or Reporter, as I like to call it) that can manage these rules and classes, read the input data and process it all line by line. Such client code looks like this: # client.rb lotro = Statistician::Reporter.new(Offense, Victory) lotro.parse(File.read(ARGV[0])) Finally, we need to begin getting useful information out of all the records that have been read and parsed by the Reporter. After the data is parsed, the final bit will be to support code such as this: # client.rb num = Offense.records.size dmg = Offense.records.inject(0) { |s, x| s + x.amount.to_i } puts "Average damage inflicted: #{dmg.to_f / num}" puts Offense.records[0].class # outputs "Offense" What is going on here? The class `Offense` serves three purposes. 1. Its declaration contains the rules for offensive related records. 2. After parsing, the class method `records` returns an array of records that matched those rules. 3. Those records are instances of the class, and instance methods that match the field names (extracted from the rules) provide access to a record's data. Hopefully this isn't too confusing. I could have broken up some of these responsibilities into other classes or sections of code, but since the three tasks are rather related, I thought it convenient and pleasing to group them all into the client's declared class. Below I'll give the full, sample client file I'm using, as well as the output it generates when run over the [hunter.txt][1] file we used last week. A few hints, first... 1. You are welcome to make `statistician.rb` depend on other Ruby modules. I personally found `OpenStruct` to be quite useful here. 2. Personally, I found making `Offense` inherit from `Reportable` to be the cleanest method. At least, it is in my own code. There may be other ways to accomplish this goal: by `include` or `extend` methods. If you find those techniques more appealing, please go ahead, but make a note of it in your submission, since it does require changing how client code is written. 3. Metaprogramming can get a bit tricky to explain in a couple sentences, so I'll leave such hints and discussion for the mailing list. Aside from that, there are some good examples of metaprogramming looking back through past Ruby Quizzes. Of particular interest would be the [metakoans.rb quiz][2]. 4. Finally, my own solution for this week's quiz is just under 80 lines long, so it need not be overly complex to support the client file below. ((( Remainder of this Ruby Quiz to come as a reply to this message. )))
|
Pages: 1 Prev: initialize and super with parameters Next: Statistician II (#168) |