From: Austin Ziegler Date: 2004-08-04T14:00:37+09:00 Subject: Re: Macros in Ruby On Wed, 4 Aug 2004 08:26:32 +0900, Jesse Jones wrote: > In article <1091501355.227383.29355.nullmailer@picachu.netlab.jp> , > Yukihiro Matsumoto wrote: >> In message "Re: Macros in Ruby" >> on 04/08/03, James Britt writes: >>| Would true macros in Ruby be more prone to abuse than they are in Lisp? >> * Lisp does not have syntax. They only have meta syntax (i.e. >> S-expression), Lisp macro do not change its (meta) syntax to >> rely on (I'm ignoring reader macro here). In comparison, Ruby >> does have syntax but no meta syntax, so that you can easily >> loose syntax to rely on by macros in Ruby. You will feel like a >> stranger when you see Ruby programs with a lot of macros. I >> don't think you feel same way when you see Lisp programs with >> macros. > > I don't buy this argument at all. It's not like good Ruby > programmers would all of a sudden use macros to rewrite the Ruby > grammar. Instead they would augment the grammar with productions > that make sense for their applications. > > For example, suppose you're writing a game and you have a lot of > code that selects behavior based on percentages (75% of hits are > normal blows, 20% are critical hits, and 5% kill your foe > out-right for example). Even in a language as expressive as Ruby > this is a bit awkward to write and not especially intentional. But > with macros it can be written very cleanly: > > percent-case > 0.75 case > normal_hit > end > > 0.20 case > critical_hit > end > > otherwise > kill_foe > end > end I'm sorry, but I don't see how this is more readable than: case hit_roll when (0.0 .. 0.75) normal_hit when (0.75 .. 0.95) critical_hit else kill_foe end end Yes, I've switched the percentages to Ranges, but there's no reason that you couldn't build a similar mechanism into one of your own classes: class Attacker def initialize @total = 0 @results = {} yield self if block_given? end def add_result(percentage, &block) if (percentage > 100) or (@total + percentage > 100) raise ArgumentError end percentage_range = (@total .. (@total + percentage)) @results[percentage_range] = block @total += percentage end def hit(percentage) if percentage.kind_of?(Float) percentage = (percentage * 100).to_i end res = nil @results.each_key do |rr| if rr === percentage res = @results[rr].call(percentage) break end end res end end hero = Attacker.new do |hh| hh.add_result(75) { |p| puts "Normal hit with #{p} roll." } hh.add_result(20) { |p| puts "Critical hit with #{p} roll." } hh.add_result(5) { |p| puts "Deadly hit with #{p} roll." } end (0 ... 20).each { hero.hit(rand) } # Result: Normal hit with 60 roll. Normal hit with 5 roll. Normal hit with 75 roll. Normal hit with 74 roll. Normal hit with 43 roll. Normal hit with 10 roll. Normal hit with 8 roll. Normal hit with 21 roll. Normal hit with 31 roll. Deadly hit with 99 roll. Normal hit with 50 roll. Normal hit with 47 roll. Normal hit with 48 roll. Normal hit with 72 roll. Normal hit with 3 roll. Critical hit with 82 roll. Normal hit with 48 roll. Critical hit with 86 roll. Critical hit with 76 roll. Normal hit with 3 roll. -austin -- Austin Ziegler * halostatue@gmail.com * Alternate: austin@halostatue.ca