Am 21.06.2007 um 12:08 schrieb dblack / wobblini.net:

> Hi --
>
> On Thu, 21 Jun 2007, Alexander Presber wrote:
>
>> Am 21.06.2007 um 11:20 schrieb Robert Klemme:
>>
>>> On 21.06.2007 11:06, Alexander Presber wrote:
>>>>>> Hello everybody,
>>>>>> Could you please take a look at the result of the following  
>>>>>> statements:
>>>>>> irb(main):001:0> a = {'foo' => 'bar', 'baz' => 'qux'}
>>>>>> => {"baz"=>"qux", "foo"=>"bar"}
>>>>>> irb(main):002:0> a.reject{|k,v| k=='foo' }
>>>>>> => {"baz"=>"qux"}
>>>>>> irb(main):003:0> a.select{|k,v| k=='baz' }
>>>>>> => [["baz", "qux"]]
>>>>>> The result of the reject statement is clearly sensible: the  
>>>>>> original
>>>>>> hash minus the element with the key 'foo'.
>>>>>> But what about select? Shouldn't it return the same hash  
>>>>>> (instead of
>>>>>> an array of key-value pairs)?
>>>>> I have to concur. I've never liked that. You'd think there'd be  
>>>>> some
>>>>> way to have Enumerable act in accordance with the class it is
>>>>> effecting, rather then dropping to the "LCD" --an array.
>>>> Absolutely.
>>>> But the most baffling to me is, that it does not even act  
>>>> _consistently_, see my original examples.
>>>> From a logical point of view (or at least the principle of least  
>>>> surprise), selecting some elements should be identical
>>>> to dropping the others.
>>>> Is there anybody who can explain, why it had to be implemented  
>>>> that way?
>>>> Is there any chance of that getting changed in the future?
>>
>> Sorry for the empty post, to much clicking without thinking.
>>
>>> The #select in Enumerable can only return a "general" type - in  
>>> this case an Array.
>>
>> I see, but isn't that restating the problem?
>
> I think Robert's point is that nothing other than a general type makes
> sense.  For example, if you select from an IO stream, you don't expect
> another IO stream.

Mhm. I thought IO#select is mixed in from the module Kernel, not  
Enumerable. In this case, select can return whatever Kernel considers  
appropriate.

I would like to think of the Enumerable mixin as an interface,  
describing the general concept of "enumerability".
Rejecting and selecting certain elements of an enumerable set  
certainly belong to this concept.

As I see it, a general recipe for selecting would be:
1) iterate (hence the need for enumerability) vfer the given set and  
check the return value of the block for each element.
2) if true, keep, otherwise drop
3) return the resulting set, keeping all other properties (including  
class) intact

Rejecting is just the opposite for 2).

>> Even though "Programming Ruby" states: [snip] Enumerable#reject:  
>> returns an array for all elements of enumObj for which block is  
>> false (see also Enumerable#find_all) [/snip],
>> reject _respects_ the class it acts upon in returning a _hash_,  
>> not an array.
>> Not so select.
>> Do you see the inconsistency?
>
> Hash overrides Enumerable#reject.  I'm not sure why, except possibly
> because of the relation between reject and delete_if.

>>> Making Enumerable depend on the class is not a good idea IMHO.   
>>> Maybe an implementation using #dup would help though...
>>
>> Could you please elaborate into that? I do not understand.
>>
>>> Of course Hash#select could act differently but that a) might  
>>> hurt duck typing
>>
>> I fail to see why, could you give an example?
>
> Anything that depends on the output having array-like behaviors:
>
> selected = enum.select(&select_block).flatten

I don't get what that has to do with duck typing.

The possibility to _flatten_ something requires it to be an Array- 
duck, not an Enumerable-duck. That is why flatten is part of Array.
Nobody should expect the result of Enumerable#select to be flattenable.

If however there is a sensible way to make the Enumerable an Array  
(in order to flatten it), you should go:

selected = enum.select(&select_block).to_a.flatten

(note the to_a)

Sincerely yours,
Alex