Behaviour of Enumerables reject vs. select mixed into Hash

Hi --

···

On Thu, 21 Jun 2007, Yossef Mendelssohn wrote:

On Jun 21, 5:37 am, dbl...@wobblini.net wrote:

Yes, but making a special case of Hash, as opposed to other
enumerables, is exactly what breaks duck typing. I would actually
prefer to see Hash#reject return an array.

I like having arrays be the "common currency" of select operations. I
don't think there's anything that you're prevented from doing as long
as that's the case.

David

The inconsistency of return values between Hash#reject vs. Hash#select
has bothered me for a long time. Above all, I'd want this to be
consistent, but unlike you (and I believe like the majority), I'd like
both to return a Hash.

I'm not really sure how duck typing even enters into it as a serious
concern. The blocks given to reject/select are different based on the
Enumerable type. Consider:

irb(main):017:0> %w{red green blue}.select { |elem| elem.length > 4 }
=> ["green"]
irb(main):018:0> {:red => true, :green => true, :blue => true}.select
{ |elem| elem.length > 4 }
(irb):18: warning: multiple values for a block parameter (2 for 1)
       from (irb):18
(irb):18: warning: multiple values for a block parameter (2 for 1)
       from (irb):18
(irb):18: warning: multiple values for a block parameter (2 for 1)
       from (irb):18
=>

I get an Array from both, which is wonderful if I'm expecting an Array
and I want to flatten the return value or do any other Array-specific
things. The only problem is one array is useless and wrong.

Didn't you like my evasive use of "&select_block"? :slight_smile:

David

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Hi --

I like having arrays be the "common currency" of select operations. I
don't think there's anything that you're prevented from doing as long
as that's the case.

I know you do, it's consistent with the positions you took back when
we were discussing to_splat. But I think that the case can be made
that preserving the class of these methods is MORE not less
duck-typed.

Maybe, but I'll admit that at this stage in this thread, I'm more into
the concrete question of what harm is done by having array be the
common currency. (I'm having trouble following the duck-typing stuff
beyond a certain level of abstraction.) I guess it's just never
bothered me, which is probably an excessively home-spun way to put it
but it's about the most rigorous analysis I seem to be up to right now
:slight_smile:

There's another thing, though, and that is the question of the
symmetry. I'm trying to put my figure on why it doesn't bother me
that Hash#reject returns a hash and Hash#select returns an array --
that is, I don't like the fact that Hash#reject is different from
other rejects (at least I somewhat don't), but in and of itself I
think the fact that reject is not simply defined as !select doesn't
bother me.

Maybe it's something like: rejecting means subtracting from a thing
that's already there, but selecting means creating a new collection --
which of course doesn't preclude having that collection be a hash, but
does mean that there's a threshold of deciding what the new collection
will, or could, be.

It would interesting if:

   "abc\ndef\nghi".reject {|s| /abc/.match(s) }

resulted in "def\nghi"....

David

···

On Fri, 22 Jun 2007, Rick DeNatale wrote:

On 6/21/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

hi everyone,

I am new to ruby, I want to installed ruby on workstation. But I am not a
super user and I don't have the access to /user/local. So I need to
change everything refer to /user/local/ to /home/fixpb.

I try to find every file contain /user/local under ruby directory and
change them . I change the configure.h. I change everything I can find but
still doesn't work. I am really frustrated now and need your
help. Thanks!

Regards,
Ting

···

---

This e-mail may contain confidential and/or privileged information. If you
are not the intended recipient (or have received this e-mail in error)
please notify the sender immediately and destroy this e-mail. Any
unauthorized copying, disclosure or distribution of the material in this
e-mail is strictly forbidden.

I can think of two potential solutions to this issue. Either require a
method in the enumerated class that dictates how to construct elements
of that class when enumerated (#<< might do), or we could have two
separate sets of enumerable methods, one for array elements and one
for hash key-value pairs.

In the first case:

(Note: I'm not testing this, so forgive any bugs. I just want to
convey the idea).

  module Enumerable
    def select(&blk)
      o = self.class.new
      each{|*e| o << e if blk[*e]}
    end
  end

  class Hash
    def <<(e)
      self[e[0]] = e[1]
    end
  end

The downside here is, it is less efficient and breaks backward
compatibility.

The other option would require an #each_assoc method (maybe assoc
isn't the best term, but anyhow...)

  module Enumerable
    def select_assoc(&blk)
      h = {}
      each_assoc{|k,v| h[k]=v if blk[k,v]}
      h
    end
  end

The downside here of course, is twice the number of Enumerable
methods.

T.

···

On Jun 21, 9:12 am, "Robert Dober" <robert.do...@gmail.com> wrote:

On 6/21/07, Alexander Presber <aljos...@weisshuhn.de> wrote:

> There should be a reason Enumerable was introduced as opposed to
> include it's functionality in Array?

Alexander I agree with your POV, however maybe it would do this
discussion some good to take one step back and look at the greater
picture.

Enumerable is a Mixin based on each and it implements methods like
select, map etc by using the each interface of it's client class. It
very often returns arrays than as it simply cannot do something
different - without creating a revolutionary new concept like an
Iterator but that is OffTopic

You were complaining about Hash#select returning an Array. I complain
about that too but it has nothing to do with Enumerable, Enumerable is
not used, it is implemented in Hash itself.

If we want to discuss if we like or dislike Hash#select returning an
array we shall fear to mention Enumerable as it has nothing to do with
it.
As a matter of fact it would be ridiculous - you have explained that
nicely yourself - to ask Enumerable to return Hashes.

Cheers
Robert

Nice.

T.

···

On Jun 21, 6:24 pm, dbl...@wobblini.net wrote:

It would interesting if:

   "abc\ndef\nghi".reject {|s| /abc/.match(s) }

resulted in "def\nghi"....

I'm not really sure if I'm off-base here, but the first thing you
learn in set theory is that as soon as you see symmetry, you should be
skeptical. Why is that? Well, my cop-out answer is: let the
linguists in the group answer that.

It's pretty obvious from what everybody says about how Ruby works, and
how best to utilize it, is that it's a "lambda" ... a procedure that
just floats in. But it's also clear that it's unclear. It's a
contradiction.

We ultimately need expressibility in order to have flexibility And
your language of choice gives you a decidability set. I'm using 'ity'
a lot ... sorry.

Mr. David Black once again shows us an interesting perspective:

"Maybe it's something like: rejecting means subtracting from a thing
that's already there, but selecting means creating a new collection"

David is smart ... the linguists are dumb. Don't take offense guys :slight_smile:

Todd

···

On 6/21/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

There's another thing, though, and that is the question of the
symmetry.

Why don't you do:
./configure --prefix=$HOME/ruby?

···

On 6/21/07, Ting Zhang <ting.zhang@db.com> wrote:

hi everyone,

I am new to ruby, I want to installed ruby on workstation. But I am not a
super user and I don't have the access to /user/local. So I need to
change everything refer to /user/local/ to /home/fixpb.

I try to find every file contain /user/local under ruby directory and
change them . I change the configure.h. I change everything I can find but
still doesn't work. I am really frustrated now and need your
help. Thanks!

--
Felipe Contreras

I can think of two potential solutions to this issue. Either require a
method in the enumerated class that dictates how to construct elements
of that class when enumerated (#<< might do), or we could have two
separate sets of enumerable methods, one for array elements and one
for hash key-value pairs.

In the first case:

(Note: I'm not testing this, so forgive any bugs. I just want to
convey the idea).

  module Enumerable
    def select(&blk)
      o = self.class.new
      each{|*e| o << e if blk[*e]}
    end
  end

  class Hash
    def <<(e)
      self[e[0]] = e[1]
    end
  end

The downside here is, it is less efficient and breaks backward
compatibility.

I agree. And the implementation of << would be the
But the real solution can only be

···

The other option would require an #each_assoc method (maybe assoc
isn't the best term, but anyhow...)

  module Enumerable
    def select_assoc(&blk)
      h = {}
      each_assoc{|k,v| h[k]=v if blk[k,v]}
      h
    end
  end

The downside here of course, is twice the number of Enumerable
methods.

T.

I can think of two potential solutions to this issue. Either require a
method in the enumerated class that dictates how to construct elements
of that class when enumerated (#<< might do), or we could have two
separate sets of enumerable methods, one for array elements and one
for hash key-value pairs.

In the first case:

(Note: I'm not testing this, so forgive any bugs. I just want to
convey the idea).

  module Enumerable
    def select(&blk)
      o = self.class.new
      each{|*e| o << e if blk[*e]}
    end
  end

  class Hash
    def <<(e)
      self[e[0]] = e[1]
    end
  end

I agree, that seems to solve the problem very elegantly.
The implementation of << would be the second precondition for the possibility to mix Enumerable in (the first being implementing each).
It allows for all "filter" methods to return a filtered version of the original object, class and all.

This solution is general and provides for arbitrary future classes to mix in Enumeration.

The downside here is, it is less efficient and breaks backward
compatibility.

The other option would require an #each_assoc method (maybe assoc
isn't the best term, but anyhow...)

  module Enumerable
    def select_assoc(&blk)
      h = {}
      each_assoc{|k,v| h[k]=v if blk[k,v]}
      h
    end
  end

The downside here of course, is twice the number of Enumerable
methods.

Or n times, for n classes that mix in Enumerable. That sounds bad, imho.

Sincerely yours,
Alex

>
> > There should be a reason Enumerable was introduced as opposed to
> > include it's functionality in Array?
>
> Alexander I agree with your POV, however maybe it would do this
> discussion some good to take one step back and look at the greater
> picture.
>
> Enumerable is a Mixin based on each and it implements methods like
> select, map etc by using the each interface of it's client class. It
> very often returns arrays than as it simply cannot do something
> different - without creating a revolutionary new concept like an
> Iterator but that is OffTopic
>
> You were complaining about Hash#select returning an Array. I complain
> about that too but it has nothing to do with Enumerable, Enumerable is
> not used, it is implemented in Hash itself.
>
> If we want to discuss if we like or dislike Hash#select returning an
> array we shall fear to mention Enumerable as it has nothing to do with
> it.
> As a matter of fact it would be ridiculous - you have explained that
> nicely yourself - to ask Enumerable to return Hashes.
>
> Cheers
> Robert

I can think of two potential solutions to this issue. Either require a
method in the enumerated class that dictates how to construct elements
of that class when enumerated (#<< might do), or we could have two
separate sets of enumerable methods, one for array elements and one
for hash key-value pairs.

In the first case:

(Note: I'm not testing this, so forgive any bugs. I just want to
convey the idea).

  module Enumerable
    def select(&blk)
      o = self.class.new
      each{|*e| o << e if blk[*e]}
    end
  end

  class Hash
    def <<(e)
      self[e[0]] = e[1]
    end
  end

The downside here is, it is less efficient and breaks backward
compatibility.

It is a conceptional beauty but it really sucks for performance, there
is *no* solution to our problem that is backward compatible, we
explicitly ask for backward compatibility unless we go for a choosable
Enum Mixin.

Something like
   class Hash
      include TomsEnum
   end

The other option would require an #each_assoc method (maybe assoc
isn't the best term, but anyhow...)

  module Enumerable
    def select_assoc(&blk)
      h = {}
      each_assoc{|k,v| h[k]=v if blk[k,v]}
      h
    end
  end

The downside here of course, is twice the number of Enumerable
methods.

And although I cannot imagine a case, how do we know that there are
not Enumerables that take three or fourtytwo params ;).
After all Enumerable is a Mixin and we have to be prepared that it be
mixed in, right?

Robert

···

On 6/21/07, Trans <transfire@gmail.com> wrote:

On Jun 21, 9:12 am, "Robert Dober" <robert.do...@gmail.com> wrote:
> On 6/21/07, Alexander Presber <aljos...@weisshuhn.de> wrote:

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Hi,

At Thu, 21 Jun 2007 23:45:42 +0900,
Trans wrote in [ruby-talk:256401]:

I can think of two potential solutions to this issue. Either require a
method in the enumerated class that dictates how to construct elements
of that class when enumerated (#<< might do), or we could have two
separate sets of enumerable methods, one for array elements and one
for hash key-value pairs.

Classes which use Enumerable are not only Array and Hash.
What do you expect for IO?

···

--
Nobu Nakada

Trans wrote:

  module Enumerable
    def select(&blk)
      o = self.class.new
      each{|*e| o << e if blk[*e]}
    end
  end

  class Hash
    def <<(e)
      self[e[0]] = e[1]
    end
  end

The downside here is, it is less efficient and breaks backward
compatibility.

Why the compatibility breakage? The same could be implemented to be fully backwards compatible:

    module Enumerable
      def select(&blk)
        o = respond_to?(:<<) ? self.class.new : Array.new
        each{|*e| o << e if blk[*e]}
      end
    end

Daniel

David said:

"Maybe it's something like: rejecting means subtracting from a thing
that's already there, but selecting means creating a new collection"

No need to create a new collection as an array, no harm to create a
new collection as an array, actually the ongoing discussion about this
point is most interesting and beyond my scope - well I try to learn a
maximum from it.

I should go with David's approach because I fully understand the consequences.
But I love Tom's and Rick's approach going into unchartered waters...

BTW all this does not answer OP's, Tom's and my *original* concern.
We were talking about Hash#reject and Hash#select not
Enumerable#reject and Enumerable#select, but nobody seems to notice,
sigh :frowning:
This topic would be less difficult to discuss I think.

David is smart ... the linguists are dumb. Don't take offense guys :slight_smile:

Offense, are you kidding? The only thing which bothers me from a set
theory point of view is the symmetry in your statement above.

Honestly Todd if you wanted to make a point I missed it, completely...

Robert

···

On 6/22/07, Todd Benson <caduceass@gmail.com> wrote:

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

I just added

p "abc\ndef\nghi".e_reject {|s| /abc/.match(s) }

To the end of the enum_test.rb file I posted earlier and that's exactly what
I got.

···

On 6/21/07, Trans <transfire@gmail.com> wrote:

On Jun 21, 6:24 pm, dbl...@wobblini.net wrote:

> It would interesting if:
>
> "abc\ndef\nghi".reject {|s| /abc/.match(s) }
>
> resulted in "def\nghi"....

Nice.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

IPMS/USA Region 12 Coordinator
http://ipmsr12.denhaven2.com/

Visit the Project Mercury Wiki Site
http://www.mercuryspacecraft.com/

Thanks! It works.

Regards,
Ting

"Felipe Contreras" <felipe.contreras@gmail.com>
06/21/2007 10:23 AM
Please respond to
ruby-talk@ruby-lang.org

To
ruby-talk@ruby-lang.org (ruby-talk ML)
cc

Subject
Re: how to installed ruby in diffrent directory

hi everyone,

I am new to ruby, I want to installed ruby on workstation. But I am not

a

super user and I don't have the access to /user/local. So I need to
change everything refer to /user/local/ to /home/fixpb.

I try to find every file contain /user/local under ruby directory and
change them . I change the configure.h. I change everything I can find

but

still doesn't work. I am really frustrated now and need your
help. Thanks!

Why don't you do:
./configure --prefix=$HOME/ruby?

···

On 6/21/07, Ting Zhang <ting.zhang@db.com> wrote:

--
Felipe Contreras

---

This e-mail may contain confidential and/or privileged information. If you
are not the intended recipient (or have received this e-mail in error)
please notify the sender immediately and destroy this e-mail. Any
unauthorized copying, disclosure or distribution of the material in this
e-mail is strictly forbidden.

is *no* solution to our problem that is backward compatible, we
explicitly ask for backward compatibility unless we go for a choosable

make this *incompatibility* please

module Enumerable
    def select(&blk)
      o = self.class.new
      each{|*e| o << e if blk[*e]}
    end
  end

  class Hash
    def <<(e)
      self[e[0]] = e[1]
    end
  end

The downside here is, it is less efficient and breaks backward
compatibility.

It is a conceptional beauty but it really sucks for performance

Why is that? Isn't the current implementation doing something similar, but more like

     def select(&blk)
       o = new Array
       each{|*e| o << e if blk[*e]}
     end

there is *no* solution to our problem that is backward compatible, we
explicitly ask for backward compatibility unless we go for a choosable
Enum Mixin.
Something like
  class Hash
     include TomsEnum
  end

Obviously breaking compatibilty is a very bad thing.
But without knowing for sure, I imagine Enumerables implementation should have been something more along the lines of the above from the beginning.
(I know, this is a bold claim and I'd like to see more opinions on that. But transfires approach is exactly what I was thinking of when bringing this up.)

Making Enumerable behave more agnostic to the class it is mixed in (by letting the class itself provide a method to add an "element" to an instance of itself, Enumerable becomes truly mixable into anything that provides "each" and "<<".
Then doing reject on any class that mixes in Enumerable will yield a filtered instance of that class, not Array.

That said - I think there should be no such thing as TomsEnum or any special implementation.
Enumerable is the place to define methods for all things containing enumerable elements.

The other option would require an #each_assoc method (maybe assoc
isn't the best term, but anyhow...)

  module Enumerable
    def select_assoc(&blk)
      h = {}
      each_assoc{|k,v| h[k]=v if blk[k,v]}
      h
    end
  end

The downside here of course, is twice the number of Enumerable
methods.

And although I cannot imagine a case, how do we know that there are
not Enumerables that take three or fourtytwo params ;).
After all Enumerable is a Mixin and we have to be prepared that it be
mixed in, right?

Yes, one could impossibly provide for all possible Mixees like this.

Yours,
Alex

Hmm...well for the first solution, I suppose we need a special
constructor to provide the kind of enumerable result we will be
building. In my example, I used self.class.new, by obviously that's
not always the case, so the class will need to tell us.

The second solution just has two forms of Enumerable, one which
returns an array and the other a hash. So #each_assoc might not alwasy
be defined, and thus these "hash-enumerable" methods wouldn't be
available. Or it would just deal in pairs, eg.

  $stdin.select{ |s| s =~ /^x/ }
  x123
  a123
  x890
  bcde
  #=> ["x123\n","x890\n"]

  $stdin.select_assoc{ |k,v| k =~ /^x/ }
  x123
  a123
  x890
  bcde
  #=> {"x123\n"=>"a123\n","x890\n"=>"bcde\n"]

T.

···

On Jun 21, 4:34 pm, Nobuyoshi Nakada <n...@ruby-lang.org> wrote:

Hi,

At Thu, 21 Jun 2007 23:45:42 +0900,
Trans wrote in [ruby-talk:256401]:

> I can think of two potential solutions to this issue. Either require a
> method in the enumerated class that dictates how to construct elements
> of that class when enumerated (#<< might do), or we could have two
> separate sets of enumerable methods, one for array elements and one
> for hash key-value pairs.

Classes which use Enumerable are not only Array and Hash.
What do you expect for IO?