I would’ve left it in. (For me) it makes a nice snippet more
readable.
···
On Wed, Aug 06, 2003 at 02:01:23PM +0900, daz wrote:
Arghh, there’s a redundant variable in that sig.
–
marko schulz
I would’ve left it in. (For me) it makes a nice snippet more
readable.
On Wed, Aug 06, 2003 at 02:01:23PM +0900, daz wrote:
Arghh, there’s a redundant variable in that sig.
–
marko schulz
For any numeric fields, you could just use -x, rather than x. So, to sort as
above, but in descending salary …
sorted = people.sort_by { |p| [p.last_name, p.first_name, -p.salary] }
but I can’t think of a way to get it to work for strings, for example. Maybe
if strings had a unary minus that changed every character to the character
with code (255 - ascii-code) … but that sounds like a real hack :-). Of
course, it’s not general enough, either.
On Thu, 7 Aug 2003 03:43, Mark J. Reed wrote:
On Thu, Aug 07, 2003 at 01:35:40AM +0900, Brett H. Williams wrote:
On Aug 6, Harry Ohlsen wrote:
sorted = people.sort_by { |p| [p.last_name, p.first_name, p.salary] }
I really like this. But is there a simple easy way to get a descending
sort here without reverting to sorted = people.sort { … } ?How about
sorted = people.sort_by { |p| [p.last_name, p.first_name, p.salary]
}.reverseI haven’t looked, but I suspect you can’t selectively make individual
criteria ascending/descending. Now, if we had a #stable_sort_by,
then we could chain them together to get the desired
results without crafting complex comparisons . . .
I assigned to the variable ‘p’, which is also a method in Ruby. That’s
bitten me before.
Ian
On Thu 07 Aug 2003 at 05:19:32 +0900, Harry Ohlsen wrote:
On Thu, 7 Aug 2003 03:26, Ian Macdonald wrote:
How does one use this with one’s own objects, though?
I just cooked up an example Person class to play with and then tried to
sort it as above:class Person
include Enumerableattr_accessor :first_name, :last_name, :age
def initialize(f,l,a)
@first_name = f
@last_name = l
@age = a.to_i
end
endp [d, g, i, j, p, j2, j3].sort_by { |x| [x.last_name, x.first_name, x.age]
}and I get:
./sort_by:23: undefined method `’ for #Person:0x4014e37c
(NoMethodError)I would guess that there’s something in the code you didn’t show (ie, that
defines d, g, i, j, p, j2 and j3) that’s causing this error message.
–
Ian Macdonald | I’ve already told you more than I know.
System Administrator |
ian@caliban.org |
http://www.caliban.org |
>
Jason Creighton wrote:
BTW, your original example worked fine on my version of Ruby. Another
option, of course, is to implement <=> for Person like so (untested)class Person
def <=>(other)
[ @last_name, @first_name ] <=> [ other.last_name, other.first_name ]
end
end
That’s probably something one should do, anyway. Ie, define the logical default ordering for objects of that type.
The thing I like about the sort_by approach is that it allows you to easily change the ordering on the fly.
It’s also just such a nice example of POLS!
H.
Still hacky, but general:
class Test
attr_accessor :foo, :bar
def initialize(f, b)
@foo, @bar = f, b
end
def inspect
“[#{foo}, #{bar}]”
end
end
def rev(obj)
a = obj.dup
class << a
alias old_cmp <=>
def <=>(other)
-old_cmp(other)
end
end
a
end
a = [
Test.new(“Hello”, 1),
Test.new(“World”, -1),
Test.new(“Foo”, 5),
Test.new(“bar”, 4)
]
p a
p a.sort_by {|x| x.foo}
p a.sort_by {|x| rev(x.foo)}
martin
Harry Ohlsen harryo@zip.com.au wrote:
but I can’t think of a way to get it to work for strings, for example. Maybe
if strings had a unary minus that changed every character to the character
with code (255 - ascii-code) … but that sounds like a real hack :-). Of
course, it’s not general enough, either.
Martin DeMello wrote:
Still hacky, but general:
class Test
attr_accessor :foo, :bar
def initialize(f, b)
@foo, @bar = f, b
enddef inspect
“[#{foo}, #{bar}]”
end
enddef rev(obj)
a = obj.dup
class << a
alias old_cmp <=>
def <=>(other)
-old_cmp(other)
end
end
a
enda = [
Test.new(“Hello”, 1),
Test.new(“World”, -1),
Test.new(“Foo”, 5),
Test.new(“bar”, 4)
]p a
p a.sort_by {|x| x.foo}
p a.sort_by {|x| rev(x.foo)}
However, if we try
p a.sort_by { |x| rev(x.bar) }
we get …
sort_by.rb:20:in `dup’: can’t dup Fixnum (TypeError)
I tried sticking in an
if obj.respond_to? :dup
but that didn’t help. It would appear that, while Fixnum responds to #dup, its response is “I’m sorry, Dave, I’m afraid I can’t do that.” :-(.
That doesn’t seem to make a lot of sense to me. I would have thought it made more sense for Fixnum#dup to just return self. Maybe there’s some good reason for not doing that.
Alternatively, maybe we need a way for classes to disown methods they inherit that they don’t want to (or logically shouldn’t) implement? Then, Fixnum could disown dup and the respond_to? test would work the way I was expecting.
Harry O.
Martin DeMello wrote:
Still hacky, but general:
class Test
attr_accessor :foo, :bar
def initialize(f, b)
@foo, @bar = f, b
enddef inspect
“[#{foo}, #{bar}]”
end
enddef rev(obj)
a = obj.dup
class << a
alias old_cmp <=>
def <=>(other)
-old_cmp(other)
end
end
a
enda = [
Test.new(“Hello”, 1),
Test.new(“World”, -1),
Test.new(“Foo”, 5),
Test.new(“bar”, 4)
]p a
p a.sort_by {|x| x.foo}
p a.sort_by {|x| rev(x.foo)}
Very nice idea. I like it.
However, if we try
p a.sort_by { |x| rev(x.bar) }
we get …
sort_by.rb:20:in `dup’: can’t dup Fixnum (TypeError)
This could be fixed with something like
def rev(obj)
-obj
rescue NoMethodError
a = obj.dup
class << a
alias old_cmp <=>
def <=>(other)
-old_cmp(other)
end
end
a
end
Not beautiful, but working.
I tried sticking in an
if obj.respond_to? :dup
but that didn’t help. It would appear that, while Fixnum responds to
#dup, its response is “I’m sorry, Dave, I’m afraid I can’t do that.”
:-(.That doesn’t seem to make a lot of sense to me. I would have thought
it made more sense for Fixnum#dup to just return self. Maybe there’s
some good reason for not doing that.
But then you couldn’t do things like Martin’s code, where you want to
modify a duplicate and not the original object.
Alternatively, maybe we need a way for classes to disown methods they
inherit that they don’t want to (or logically shouldn’t) implement?
Then, Fixnum could disown dup and the respond_to? test would work the
way I was expecting.
Since dup is defined in Object (Kernel), you’d expect to find it in
every object, wouldn’t you? But if you really want you can undefine
dup for Fixnums:
p 5.respond_to?( :dup ) # => true
p 5.dup rescue puts $! # => can’t dup Fixnum
Fixnum.send :undef_method, :dup
p 5.respond_to?( :dup ) # => false
p 5.dup rescue puts $! # => undefined method `dup’ for 5:Fixnum
Regards,
Pit
On 7 Aug 2003 at 8:30, Harry Ohlsen wrote:
However, if we try
p a.sort_by { |x| rev(x.bar) }
we get …
sort_by.rb:20:in `dup’: can’t dup Fixnum (TypeError)
I tried sticking in an
if obj.respond_to? :dup
but that didn’t help. It would appear that, while Fixnum responds to
#dup, its response is “I’m sorry, Dave, I’m afraid I can’t do that.”
:-(.
This fixes it:
def rev(obj)
a = obj.dup rescue (return obj.cmp_inv)
class << a
alias old_cmp <=>
def <=>(other)
-old_cmp(other)
end
end
a
end
class Fixnum
def cmp_inv
-self
end
end
where for any class not supporting dup, we explicitly define a
‘comparative inverse’ such that a<=>b = b.cmp_inv<=>a.cmp_inv for all a,
b belonging to our class.
That doesn’t seem to make a lot of sense to me. I would have thought
it made more sense for Fixnum#dup to just return self. Maybe there’s
some good reason for not doing that.
In this instance, we definitely don’t want Fixnum#dup to return self -
the call to dup is to avoid the object itself having its comparator
reversed. We could avoid the call to dup altogether by using a
delegator, I suppose
class RevCmp
attr_reader :this
def initialize(obj)
@this = obj
end
def <=>(other)
other.this <=> @this
end
end
def rev(obj)
RevCmp.new(obj)
end
Alternatively, maybe we need a way for classes to disown methods they
inherit that they don’t want to (or logically shouldn’t) implement?
Then, Fixnum could disown dup and the respond_to? test would work the
way I was expecting.
This breaks some sort of OO principle, I think.
martin
Harry Ohlsen harryo@qiqsolutions.com wrote: