[ruby-talk:02354] Re: Function of Array.filter surprises me

From: Quinn Dunkan <quinn@...>
Date: 2000-04-03 21:48:17 UTC
List: ruby-talk #2354
> mengx@nielsenmedia.com writes:
> # >
> # > I have to admit `filter' is not a best name; some proposed `collect!',
> # > but I don't feel it's not collecting anything.
> # >
> # >
> # >                                matz.
> #
> # Would convert/convert! or transform/transform! be better that
> collect/filter?
> # Of course they could mean something like "to_s", but in iterator context,
> # I will not be confused.
> 
> Well, filter once kind of made a sort of idiosyncratic sense in the distant
> past when it was a semi-common UNIX idiom for something like a
> sed/awk/nroff stage in a pipeline.

filter also makes sense to anyone who's used any language other than smalltalk
that has this function (except perl, of course, which uses 'grep' apparently
in an attempt to confuse shell programmers).  Uh, the non-destructive version,
I mean (that ruby calles `detect').

I think ruby's use of filter is non-intuitive and potentially dangerous for
everyone other than smalltalkers.  However, even though I too prefer map, I
think collect isn't so bad, and it's certainly not going anywhere.  I would
also highly discourage convert, transform, or other non-standard
not-invented-here stuff (smalltalk has a sort of excuse of being old :)

What's wrong with collect! ?  It makes sense to me:
a.sort      -> `a' sorted
a.sort!     -> `a' sorted and modifies `a' to the same value
a.flatten   -> `a' with interior sequences eliminated
a.flatten!  -> `a' with interior sequences eliminated, and modifies `a' to the
                same value
a.compact   -> a.detect {|e| e != nil}
a.compact!  -> a.detect {|e| e != nil}, and modifies `a' to the same value

It's not a huge leap for a programmer to imagine:

a.collect   -> `a' with function applied
a.collect!  -> `a' with function applied, and modifies `a' to the same value

(or map/map!, which is, after all, The Standard)

then we could also have:

a.filter    -> elts of `a' that are true for function
a.filter!   -> elts of `a' that are true for function, and modifies `a' to
               the same value

Notice a certain consistency?  *Most* of the standard methods have nice
behavior here: given a method `foo' I don't have to look in the docs to know
what `foo!' will do.  That's a big win, I think.

I'm sure the current definition of filter has some use somewhere, but I can't
think of it, especially seeing how it's destructive.  I think something like

buf.concat(@attr.filter {|v| CGI::escape(v) }.join("&"))

like you see in the stdlib would be much clearer as

@attr = @attr.collect{|v| CGI::escpae(v) }
buf.concat(@attr.join("&"))

anyway, since user filter can obscure the fact that @attr is being modified.
And for things like

values = values.split('&').filter{|v| CGI::unescape(v) }

filter is unnecessary.  Actually, 9 out of 12 uses of filter in the stdlib are
things like `foo = foo.filter' or `foo = bar.split.filter' which looks like
people don't understand the usage of filter (I don't blame 'em).  And then you
have things like `foo(bar).filter' which look real dangerous to me because you
don't know if foo() is returning a reference to an existing object or making a
new one.



While we're on the subject, what would a ruby implementation of foldr look
like?  Is there one?

foldr f (x:xs) = f x (foldr f xs)
foldr f x = x
foldr f [] = error "can't fold empty list with no initial value"

class Array
    def foldr
        if self.length == 0 then
            raise ValueError, "can't fold empty list with no initial value"
        elsif self.length == 2
            yield self[0], self[1]
        else
            yield self[0], self[1..-1].foldr
        end
    end
end

Hmm, that won't work... how do you use a recursive iterator?  Probably you
don't... I'm not thinking properly in ruby today :)

In This Thread