Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.ruby > #3288 > unrolled thread

Using the spaceship operator

Started byRichardOnRails <RichardDummyMailbox58407@USComputerGurus.com>
First post2011-04-20 21:13 -0700
Last post2011-04-26 05:08 -0500
Articles 8 — 4 participants

Back to article view | Back to comp.lang.ruby


Contents

  Using the spaceship operator RichardOnRails <RichardDummyMailbox58407@USComputerGurus.com> - 2011-04-20 21:13 -0700
    Re: Using the spaceship operator John Feminella <johnf@bitsbuilder.com> - 2011-04-20 23:21 -0500
    Re: Using the spaceship operator Brian Candler <b.candler@pobox.com> - 2011-04-21 04:41 -0500
      Re: Using the spaceship operator 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-21 12:07 -0500
    Re: Using the spaceship operator RichardOnRails <RichardDummyMailbox58407@USComputerGurus.com> - 2011-04-21 06:48 -0700
      Re: Using the spaceship operator Brian Candler <b.candler@pobox.com> - 2011-04-21 12:26 -0500
    Re: Using the spaceship operator RichardOnRails <RichardDummyMailbox58407@USComputerGurus.com> - 2011-04-21 16:56 -0700
      Re: Using the spaceship operator Brian Candler <b.candler@pobox.com> - 2011-04-26 05:08 -0500

#3288 — Using the spaceship operator

FromRichardOnRails <RichardDummyMailbox58407@USComputerGurus.com>
Date2011-04-20 21:13 -0700
SubjectUsing the spaceship operator
Message-ID<084e58ba-184f-470c-a341-68bfecc192a9@w36g2000vbi.googlegroups.com>
Hi,

Here an abstract version of my app, in which I have an array of two-
element arrays I'd like to sort on the second element of each pair
using the spaceship operator:

$data = [ [:A, 12], [:B, 30], [:C, 4] ]

Class MyTest < Array
    def <=>(other)
	self[1] <=> other[1]
    end

    @sorted_array = $date
 end

How can I make this work?

Thanks in advance,
Richard

[toc] | [next] | [standalone]


#3289

FromJohn Feminella <johnf@bitsbuilder.com>
Date2011-04-20 23:21 -0500
Message-ID<BANLkTimgjtGo37sqxhS1LciaAadrq_AVUg@mail.gmail.com>
In reply to#3288
Instead of doing it that way, consider using sort_by instead to sort
by the second element:

ruby-1.9.2-p180 :001 > fruits = [[:apple, 100], [:banana, 150],
[:tomato, 80], [:kumquat, 180]]
 => [[:apple, 100], [:banana, 150], [:tomato, 80], [:kumquat, 180]]

ruby-1.9.2-p180 :002 > fruits.sort_by { |f| f[1] }
 => [[:tomato, 80], [:apple, 100], [:banana, 150], [:kumquat, 180]]

~ jf
--
John Feminella
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: http://stackoverflow.com/users/75170/



On Thu, Apr 21, 2011 at 00:15, RichardOnRails
<RichardDummyMailbox58407@uscomputergurus.com> wrote:
> Hi,
>
> Here an abstract version of my app, in which I have an array of two-
> element arrays I'd like to sort on the second element of each pair
> using the spaceship operator:
>
> $data = [ [:A, 12], [:B, 30], [:C, 4] ]
>
> Class MyTest < Array
>    def <=>(other)
>        self[1] <=> other[1]
>    end
>
>    @sorted_array = $date
>  end
>
> How can I make this work?
>
> Thanks in advance,
> Richard
>
>

[toc] | [prev] | [next] | [standalone]


#3302

FromBrian Candler <b.candler@pobox.com>
Date2011-04-21 04:41 -0500
Message-ID<d2c6f554ef89586d8f63c12ccc500bc2@ruby-forum.com>
In reply to#3288
RichardOnRails wrote in post #994206:
> Class MyTest < Array
>     def <=>(other)
>   self[1] <=> other[1]
>     end
>
>     @sorted_array = $date
>  end
>
> How can I make this work?

Your way *can* be made to work, but it's ugly.

class MyElement < Array
  def <=>(other)
    self[1] <=> other[1]
  end
end

a = [ MyElement.new([:A,12]),
      MyElement.new([:B,30]),
      MyElement.new([:C,4]),
    ]
a.sort!
p a

The point is that the <=> operator is applied to pairs of elements in 
the array, not to the container object itself.

As a general rule I'd say: don't subclass standard Ruby classes. Use 
delegation instead. So if you want a container object with Array-like 
properties but special behaviour, then make it have an Array, rather 
than be an Array.

class MyContainer
  def initialize(a = [])
    @a = a
  end
  def sort!
    @a = @a.sort_by { |elem| elem[1] }
  end
  ... delegate other methods as required,
  ... or use SimpleDelegator
end

In the longer term you'll find this a much more flexible way of 
composing objects and behaviour. The only reason programming classes 
tell you to subclass is because of the inflexible typing systems in 
certain other languages.

Ruby has "duck typing": there is no need for your class to inherit from 
Array, it just has to implement the behaviour of Array that you're 
interested in.

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [next] | [standalone]


#3321

From7stud -- <bbxx789_05ss@yahoo.com>
Date2011-04-21 12:07 -0500
Message-ID<b0fde9907b8837d145e87b0dc2756c1a@ruby-forum.com>
In reply to#3302
Brian Candler wrote in post #994252:
>
> The point is that the <=> operator is applied to pairs of elements in
> the array, not to the container object itself.
>

In other words, your spaceship operator only gets applied to objects of 
your class--not any ole' array.

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [next] | [standalone]


#3313

FromRichardOnRails <RichardDummyMailbox58407@USComputerGurus.com>
Date2011-04-21 06:48 -0700
Message-ID<1e803fee-d137-48bc-9d7e-5e3ff93089a8@a26g2000vbo.googlegroups.com>
In reply to#3288
On Apr 21, 12:13 am, RichardOnRails
<RichardDummyMailbox58...@USComputerGurus.com> wrote:
> Hi,
>
> Here an abstract version of my app, in which I have an array of two-
> element arrays I'd like to sort on the second element of each pair
> using the spaceship operator:
>
> $data = [ [:A, 12], [:B, 30], [:C, 4] ]
>
> Class MyTest < Array
>     def <=>(other)
>         self[1] <=> other[1]
>     end
>
>     @sorted_array = $date
>  end
>
> How can I make this work?
>
> Thanks in advance,
> Richard

Hi John and Brian,

Thank you very much for your extremely insightful answers.

I've been oscillating between:
	Metaprogramming Ruby
	The Well-Oriented Rubyist
	Design Patterns in Ruby

But I didn't see anything in them for my sorting problem, so I Googled
for it.  Thus I  found this concise method, but I couldn't figure out
how to make "self" be my array of arrays.

John: Thanks for that sort_by method using a block to return the
specific value (from each element) I want the sort method to use.
Very tidy!

Brian: Thanks for
1. addressing my key issue about how to make "self" be my array, so to
speak.  Question,  would that redefinition of <=> contaminate uses of
"sort" in other top-level classes?  I could test for an answer, but I
might miss something.
2. using "delegation", which I've seen in those textbooks but not yet
internalized.

Best wishes,
Richard

[toc] | [prev] | [next] | [standalone]


#3325

FromBrian Candler <b.candler@pobox.com>
Date2011-04-21 12:26 -0500
Message-ID<b364c46c94653d34da0a4f4b0f184623@ruby-forum.com>
In reply to#3313
RichardOnRails wrote in post #994287:
> Brian: Thanks for
> 1. addressing my key issue about how to make "self" be my array, so to
> speak.  Question,  would that redefinition of <=> contaminate uses of
> "sort" in other top-level classes?

No. I only redefined <=> within the class MyElement, so it only affects 
what happens when you call <=> on an object of class MyElement.

When you call sort on an Array (that is, your outer array which includes 
the pairs), internally it does a quicksort. The quicksort calls a.<=>(b) 
for various pairs of elements a and b within the array.

So what matters is that the elements *within* the array implement <=> 
for comparing themselves against any other element.

> 2. using "delegation", which I've seen in those textbooks but not yet
> internalized.

Basically it means passing through method calls to the underlying 
object. You can do this explicitly for each method of interest:

class MyElement
  def initialize(a)
    @a = a
  end
  def size
    @a.size
  end
  def [](index)
    @a[index]
  end
  def []=(index,val)
    @a[index] = val
  end
  ... etc
end

This gives you an opportunity to customise the behaviour in any way you 
like, or have one method call combine the results from invoking multiple 
underlying objects, or whatever you like.

If you find it tedious to repeat lots of method definitions, then you 
can look at method_missing, or delegate.rb in the Ruby standard library. 
There are examples in the source code. In this case:

require 'delegate'
class MyElement < DelegateClass(Array)
  def <=>(other)
    self[1] <=> other[1]
  end
end

This creates a new class which passes all unknown methods to the 
underlying object.

a = MyElement.new([:x, :y])
puts a[1]

You have called the '[]' method on your object of class MyElement, and 
it's automatically passed through to the '[]' method on the underlying 
Array object.

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [next] | [standalone]


#3347

FromRichardOnRails <RichardDummyMailbox58407@USComputerGurus.com>
Date2011-04-21 16:56 -0700
Message-ID<4aa8c4b1-e8e8-4726-9e20-42901336857d@m40g2000vbt.googlegroups.com>
In reply to#3288
7stud: Thanks for your (comforting :BG) explanation
Brian: Thanks for your expanded explanation

While I'm still playing with delegation,  I've taken "ownership" of a
couple of the ideas previously offered, most notably "sort_by" and:
class Meta < Array
  def <=>(other)
    self[1] <=> other[1]
  end
end

a = []
[ [:A,12], [:B,30], [:C,4]  ]. each { |element| a <<
Meta.new(element) }
p a.sort

I'm eternally grateful for this website.  I'm a retired programmer
relatively new to Ruby and Rails.  I'd quit programming except for the
generosity of members of this NG.

Best wishes,
Richard

[toc] | [prev] | [next] | [standalone]


#3508

FromBrian Candler <b.candler@pobox.com>
Date2011-04-26 05:08 -0500
Message-ID<81fdf3e2a9ff50645fd3988c0acd6630@ruby-forum.com>
In reply to#3347
RichardOnRails wrote in post #994401:
> While I'm still playing with delegation,  I've taken "ownership" of a
> couple of the ideas previously offered, most notably "sort_by" and:
> class Meta < Array
>   def <=>(other)
>     self[1] <=> other[1]
>   end
> end
>
> a = []
> [ [:A,12], [:B,30], [:C,4]  ]. each { |element| a <<
> Meta.new(element) }
> p a.sort

And a slightly tidier way is to use 'map' (aliased as 'collect') which 
iterates and accumulates the output array in one go:

a = [ [:A,12], [:B,30], [:C,4] ].map { |element| Meta.new(element) }
p a.sort

Regards,

Brian.

-- 
Posted via http://www.ruby-forum.com/.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.ruby


csiph-web