Hello -- On Sat, 1 Sep 2001, Joel VanderWerf wrote: > David Alan Black wrote: > > > I'm still playing around with this :-) but one possible source of > > problems is that you use a boolean test to end the series... which > > means, for example, that if you change this: > > > >> tree = [[0, [1, 2]], 3, [4, 5, [6]]] > > > > to this: > > > > tree = [[0, [1, 2]], 3, [4, 5, [6,7,nil]]] > > > > it will not process tree[2][2][2]. > > Hi, David, > > Good point. Maybe it should automatically check for respond_to? before > sending the message. You can do that manually: > > tree = [[0, [1, 2]], 3, [4, 5, [6, 7, nil]]] > for node in tree.by { |x| x.respond_to?(:at) && x.at(2) } > p node > end > > To handle this automatically, maybe catching an exception is faster > than checking for respond_to? How about this change: > > def each > cur = @first > if @next_name > begin > loop do > break unless cur That still has the test-for-nil behavior, though. > yield cur > cur = cur.send @next_name, *@args > end > rescue NameError > end > elsif @next_proc > .... What I'm finding is that it's hard to automate because there are different things that could cause the exception. For instance, in your first example (or some approximation thereof :-) list = Foo.new(0, Foo.new(1, Foo.new(2, Foo.new(3)))) list.by(:next_foo).each do |f| p f.value end the problem is that the last Foo's next_foo is nil, so the call to #value, if allowed to happen, raises an exception. I don't think the linked list class should have to figure that out, since its job is just to cycle through all the next_foo's. And the last Foo does respond to next_foo.... it just happens to return nil. Then there's the kind of case where one types 'att' instead of 'at'. > Although I wouldn't want to do this without checking somehow that the > exception was generated by the send in this method, rather than something > called as a result of the send operation. Yeah, that :-) Anyway, for what it's worth, here's what I've been working on/with. It's sketchy, and it still has the test-for-nil problem. My goal was just to tighten it up a little. Also, I have, at least for testing purposes, eliminated the calling of the first arg when it's a proc. (So one of your tests, the f = proc... one, doesn't work with this version.) I've also taken it out of Enumerable. class LinkedListDelegator include Enumerable def initialize(first, next_spec, *args, &block) @cur = first @action = if block then block else proc {|t| t.send(next_spec, *args)} end end def each while @cur # <---- not good yield @cur @cur = @action[@cur] end self end end class Object def by(next_name = nil, *args, &next_getter) LinkedListDelegator.new(self, next_name, *args, &next_getter) end end David -- David Alan Black home: dblack / candle.superlink.net work: blackdav / shu.edu Web: http://pirate.shu.edu/~blackdav