|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
Which approach is better: parametric module or an injecting class
method. # Generates identity/key methods based on specified attributes. # # include EquateOn(:a, :b) # # is equivalent to including a module containing: # # def ==(other) # self.a == other.a && self.b == other.b # end # # def eql?(other) # self.a.eql?(other.a) && self.b.eql?(other.b) # end # # def hash() # self.a.hash ^ self.b.hash # end # def EquateOn(*fields) code = "" code << "def ==(o) " << fields.map {|f| "self.#{f} == o.#{f}" }.join(" && ") << " end\n" code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql? (o.#{f})" }.join(" && ") << " end\n" code << "def hash() " << fields.map {|f| "self.#{f}.hash" }.join(" ^ ") << " end\n" mod = Module.new mod.module_eval( code ) mod end - Or - # Generates identity/key methods based on specified attributes. # # equate_on :a, :b # # _is equivalent to_ # # def ==(o) # self.a == o.a && self.b == o.b # end # # def eql?(o) # self.a.eql?(o.a) && self.b.eql?(o.b) # end # # def hash() # self.a.hash ^ self.b.hash # end def equate_on(*fields) code = "" code << "def ==(o) " << fields.map {|f| "self.#{f} == o.#{f}" }.join(" && ") << " end\n" code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql? (o.#{f})" }.join(" && ") << " end\n" code << "def hash() " << fields.map {|f| "self.#{f}.hash" }.join(" ^ ") << " end\n" class_eval( code ) fields end T. |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
2008/3/13, Trans <transfire@gmail.com>:
> Which approach is better: parametric module or an injecting class > method. Are you writing a book on best practices? There seem to be quite a few of these questions recently. :-)) > # Generates identity/key methods based on specified attributes. > # > # include EquateOn(:a, :b) > # > # is equivalent to including a module containing: > # > # def ==(other) > # self.a == other.a && self.b == other.b > # end > # > # def eql?(other) > # self.a.eql?(other.a) && self.b.eql?(other.b) > # end > # > # def hash() > # self.a.hash ^ self.b.hash > # end > # > > def EquateOn(*fields) > code = "" > code << "def ==(o) " << fields.map {|f| "self.#{f} == > o.#{f}" }.join(" && ") << " end\n" > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql? > (o.#{f})" }.join(" && ") << " end\n" > code << "def hash() " << fields.map {|f| > "self.#{f}.hash" }.join(" ^ ") << " end\n" > mod = Module.new > mod.module_eval( code ) > mod > end > > - Or - > > # Generates identity/key methods based on specified attributes. > # > # equate_on :a, :b > # > # _is equivalent to_ > # > # def ==(o) > # self.a == o.a && self.b == o.b > # end > # > # def eql?(o) > # self.a.eql?(o.a) && self.b.eql?(o.b) > # end > # > # def hash() > # self.a.hash ^ self.b.hash > # end > > def equate_on(*fields) > code = "" > code << "def ==(o) " << fields.map {|f| "self.#{f} == > o.#{f}" }.join(" && ") << " end\n" > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql? > (o.#{f})" }.join(" && ") << " end\n" > code << "def hash() " << fields.map {|f| > "self.#{f}.hash" }.join(" ^ ") << " end\n" > class_eval( code ) > fields > end I opt for the second solution because the anonymous module does not have any reuse effects - unless you cache it based on field names. :-) Kind regards robert -- use.inject do |as, often| as.you_can - without end |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
On Mar 13, 4:42 am, "Robert Klemme" <shortcut...@googlemail.com> wrote: > 2008/3/13, Trans <transf...@gmail.com>: > > > Which approach is better: parametric module or an injecting class > > method. > > Are you writing a book on best practices? There seem to be quite a > few of these questions recently. :-)) Ha... I probably should be! But right now I'm just working through some old "TODO" questions in Facets. > > # Generates identity/key methods based on specified attributes. > > # > > # equate_on :a, :b > > # > > # _is equivalent to_ > > # > > # def ==(o) > > # self.a == o.a && self.b == o.b > > # end > > # > > # def eql?(o) > > # self.a.eql?(o.a) && self.b.eql?(o.b) > > # end > > # > > # def hash() > > # self.a.hash ^ self.b.hash > > # end > > > def equate_on(*fields) > > code = "" > > code << "def ==(o) " << fields.map {|f| "self.#{f} == > > o.#{f}" }.join(" && ") << " end\n" > > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql? > > (o.#{f})" }.join(" && ") << " end\n" > > code << "def hash() " << fields.map {|f| > > "self.#{f}.hash" }.join(" ^ ") << " end\n" > > class_eval( code ) > > fields > > end > > I opt for the second solution because the anonymous module does not > have any reuse effects - unless you cache it based on field names. :-) And if we do cache based on field names?T. |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
2008/3/13, Trans <transfire@gmail.com>:
> > > On Mar 13, 4:42 am, "Robert Klemme" <shortcut...@googlemail.com> > wrote: > > 2008/3/13, Trans <transf...@gmail.com>: > > > > > > Which approach is better: parametric module or an injecting class > > > method. > > > > Are you writing a book on best practices? There seem to be quite a > > few of these questions recently. :-)) > > > Ha... I probably should be! But right now I'm just working through > some old "TODO" questions in Facets. LOL > > > # Generates identity/key methods based on specified attributes. > > > # > > > # equate_on :a, :b > > > # > > > # _is equivalent to_ > > > # > > > # def ==(o) > > > # self.a == o.a && self.b == o.b > > > # end > > > # > > > # def eql?(o) > > > # self.a.eql?(o.a) && self.b.eql?(o.b) > > > # end > > > # > > > # def hash() > > > # self.a.hash ^ self.b.hash > > > # end > > > > > def equate_on(*fields) > > > code = "" > > > code << "def ==(o) " << fields.map {|f| "self.#{f} == > > > o.#{f}" }.join(" && ") << " end\n" > > > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql? > > > (o.#{f})" }.join(" && ") << " end\n" > > > code << "def hash() " << fields.map {|f| > > > "self.#{f}.hash" }.join(" ^ ") << " end\n" > > > class_eval( code ) > > > fields > > > end > > > > I opt for the second solution because the anonymous module does not > > have any reuse effects - unless you cache it based on field names. :-) > > > And if we do cache based on field names?Aw, com' on. Interesting additional question: should order matter? I'd say probably yes, but that will reduce reuse of cached entries. Kind regards robert PS: if you run out of tasks (I know, won't happen) - I just discovered this site: http://www.virginmedia.com/movies/dv...ator/index.php -- use.inject do |as, often| as.you_can - without end |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
On Mar 13, 1:23 pm, "Robert Klemme" <shortcut...@googlemail.com> wrote: > 2008/3/13, Trans <transf...@gmail.com>: > > > > > > > On Mar 13, 4:42 am, "Robert Klemme" <shortcut...@googlemail.com> > > wrote: > > > 2008/3/13, Trans <transf...@gmail.com>: > > > > > Which approach is better: parametric module or an injecting class > > > > method. > > > > Are you writing a book on best practices? There seem to be quite a > > > few of these questions recently. :-)) > > > Ha... I probably should be! But right now I'm just working through > > some old "TODO" questions in Facets. > > LOL > > > > > > > # Generates identity/key methods based on specified attributes. > > > > # > > > > # equate_on :a, :b > > > > # > > > > # _is equivalent to_ > > > > # > > > > # def ==(o) > > > > # self.a == o.a && self.b == o.b > > > > # end > > > > # > > > > # def eql?(o) > > > > # self.a.eql?(o.a) && self.b.eql?(o.b) > > > > # end > > > > # > > > > # def hash() > > > > # self.a.hash ^ self.b.hash > > > > # end > > > > > def equate_on(*fields) > > > > code = "" > > > > code << "def ==(o) " << fields.map {|f| "self.#{f} == > > > > o.#{f}" }.join(" && ") << " end\n" > > > > code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql? > > > > (o.#{f})" }.join(" && ") << " end\n" > > > > code << "def hash() " << fields.map {|f| > > > > "self.#{f}.hash" }.join(" ^ ") << " end\n" > > > > class_eval( code ) > > > > fields > > > > end > > > > I opt for the second solution because the anonymous module does not > > > have any reuse effects - unless you cache it based on field names. :-) > > > And if we do cache based on field names?> > Aw, com' on. Interesting additional question: should order matter? > I'd say probably yes, but that will reduce reuse of cached entries. I explored the cache idea a bit more, and it made me see why a Module approach appealed to me over the injection method, but generating parametric modules, even if cached seemed somehow wrong too. I derived this instead: module EquateOn def ==(o) equate_fields.all?{ |f| send(f) == o.send(f) } end def eql?(o) equate_fields.all?{ |f| send(f).eql?(o.send(f)) } end def hash equate_fields.inject(0){ |memo, f| memo ^ send(f).hash } end end def EquateOn(*fields) define_method(:equate_fields){ fields } return EquateOn end Much better, but I still wonder if it is enough to be preferable over the a er method. T. |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
On Mar 12, 2008, at 7:14 PM, Trans wrote: > Which approach is better: parametric module or an injecting class > method. i would say neither and both. it's saner to decouple them and then recouple - giving the best of both worlds with the same amount of code: cfp2:~ > cat a.rb module Equate module Methods def equate a, b module_eval <<-code def ==(other) self.#{ a } == other.#{ a } && self.#{ b } == other.#{ b } end def eql?(other) self.#{ a }.eql?(other.#{ a }) && self.#{ b }.eql? (other.#{ b }) end def hash() self.#{ a }.hash ^ self.#{ b }.hash end code end end def Equate.included other other.send :extend, Methods end def Equate.on a, b Module.new{ include Equate equate a, b } end end class C < Struct.new(:a, :b) include Equate.on(:a, :b) end p C[4,2] == C[4,2] #=> true p C[4,2] == C[4,3] #=> false class D < Struct.new(:a, :b) include Equate equate :a, :b end p D[4,2] == D[4,2] #=> true p D[4,2] == D[4,3] #=> false cfp2:~ > ruby a.rb true false true false a @ http://codeforpeople.com/ -- share your knowledge. it's a way to achieve immortality. h.h. the 14th dalai lama |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
On 13.03.2008 20:35, ara howard wrote:
> On Mar 12, 2008, at 7:14 PM, Trans wrote: > >> Which approach is better: parametric module or an injecting class >> method. > > i would say neither and both. it's saner to decouple them and then > recouple - giving the best of both worlds with the same amount of code: > > > cfp2:~ > cat a.rb > module Equate > module Methods > def equate a, b > module_eval <<-code > def ==(other) > self.#{ a } == other.#{ a } && self.#{ b } == other.#{ b } > end > def eql?(other) > self.#{ a }.eql?(other.#{ a }) && self.#{ b }.eql? > (other.#{ b }) > end > def hash() > self.#{ a }.hash ^ self.#{ b }.hash > end > code > end > end > > def Equate.included other > other.send :extend, Methods > end > > def Equate.on a, b > Module.new{ > include Equate > equate a, b > } > end > end > > class C < Struct.new(:a, :b) > include Equate.on(:a, :b) > end > > p C[4,2] == C[4,2] #=> true > p C[4,2] == C[4,3] #=> false > > class D < Struct.new(:a, :b) > include Equate > equate :a, :b > end > > p D[4,2] == D[4,2] #=> true > p D[4,2] == D[4,3] #=> false > > > > cfp2:~ > ruby a.rb > true > false > true > false Using a Struct generated class as base class is a bad example IMHO because those classes do already have the equation properties. At the moment the only advantage I see in using modules is avoidance of namespace pollution. Personally I'd just have a method in class Module equate_on which defines methods as shown. Btw, while we're at it, I'd also like order_on which defines <=> based on fields given. :-) Kind regards robert |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
On Mar 13, 2008, at 4:20 PM, Robert Klemme wrote: > Using a Struct generated class as base class is a bad example IMHO > because those classes do already have the equation properties. heh - yeah bad example. i just used struct to avoid defining atts 'a' and 'b' and an initializer - i think the example stands with that though. > At the moment the only advantage I see in using modules is avoidance > of namespace pollution. Personally I'd just have a method in class > Module equate_on which defines methods as shown. well namspace pollution is no small thing! the other huge advantage is that running rdoc over something like class C module ClassMethods end end is about a billion times better than running it over class C singleton_class.module_eval .... not to mention grep et all. the other huge advantage of using modules is subtle: if you start out with code such as module M def self.included other add_equate_method_to other end end and, over the course of your project end up with module M X = 4 Y = 2 end then the result of class C include M end is that X and Y are dumped into C *even though the included block was purposely setup to define what happens logically for module inclusion*. by carving out the target of inclusion one can do module M module Methods end def self.included other other.extend Methods end end and dump constants into M to your heart's content without inadvertently dumping them into every class that wanted a little class method. this last bit i just recently have been using - but born out from long debugging session ;-) cheers. a @ http://drawohara.com/ -- sleep is the best meditation. h.h. the 14th dalai lama |
|
![]() |
| Outils de la discussion | |
|
|