What's New and Shiny in Ruby 1.8.0?

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]
}.reverse

I 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 . . . :slight_smile:

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 Enumerable

attr_accessor :first_name, :last_name, :age

def initialize(f,l,a)
@first_name = f
@last_name = l
@age = a.to_i
end
end

p [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
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)}

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
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)}

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

not delegating anything else because this is explicitly a throwaway

object used only inside a sort_by block

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: