I wanted to write a simple method for comparing two paths on a Windows
system. My initial algorithm felt very contrived:
def same_path?(a, b)
a, b = [a, b].map{ |p| File.expand_path(p).downcase }
a == b
end
It felt like saving the result from #map and then doing the comparison
shouldn't be necessary. So I came up with the following solution:
class Array
def apply(method)
shift.send(method, *self)
end
end
This allowed me to define #same_path? thusly:
def same_path?(a, b)
[a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end
which, to me, looks a lot nicer. Array#apply takes a method (in a
symbolic manner) and applies it to its first element, passing the rest
of the elements as arguments to the given method.
Any thoughts? Is Array#apply a method that could potentially have
other uses than my specific example above?
Your method should be called "apply!" because it's destructive. I'd
rather not do it that way, i.e. without changing the array at hand.You
can use inject for that.
I'd use #inject for your problem:
match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}
Yet another solution is to use one of Pathname's multiple methods (for
example #realpath).
I wanted to write a simple method for comparing two paths on a Windows
system. My initial algorithm felt very contrived:
def same_path?(a, b)
a, b = [a, b].map{ |p| File.expand_path(p).downcase }
a == b
end
It felt like saving the result from #map and then doing the comparison
shouldn't be necessary. So I came up with the following solution:
class Array
def apply(method)
shift.send(method, *self)
end
end
This allowed me to define #same_path? thusly:
def same_path?(a, b)
[a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end
which, to me, looks a lot nicer. Array#apply takes a method (in a
symbolic manner) and applies it to its first element, passing the rest
of the elements as arguments to the given method.
Any thoughts? Is Array#apply a method that could potentially have
other uses than my specific example above?
Yes, not that it can't be fixed, but the current definition is sort of
broken (on all systems):
···
On Aug 14, 11:50 am, Eric Hodel <drbr...@segment7.net> wrote:
On Aug 14, 2007, at 02:40, nikolai.weib...@gmail.com wrote:
> I wanted to write a simple method for comparing two paths on a Windows
> system.
Is there something wrong with Pathname#==?
#
# Compare this pathname with +other+. The comparison is string-
based.
# Be aware that two different paths (<tt>foo.txt</tt> and <tt>./
foo.txt</tt>)
# can refer to the same file.
#
def ==(other)
return false unless Pathname === other
other.to_s == @path
end
alias === ==
alias eql? ==
> I wanted to write a simple method for comparing two paths on a Windows
> system. My initial algorithm felt very contrived:
> def same_path?(a, b)
> a, b = [a, b].map{ |p| File.expand_path(p).downcase }
> a == b
> end
> It felt like saving the result from #map and then doing the comparison
> shouldn't be necessary. So I came up with the following solution:
> class Array
> def apply(method)
> shift.send(method, *self)
> end
> end
> This allowed me to define #same_path? thusly:
> def same_path?(a, b)
> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> end
> which, to me, looks a lot nicer. Array#apply takes a method (in a
> symbolic manner) and applies it to its first element, passing the rest
> of the elements as arguments to the given method.
> Any thoughts? Is Array#apply a method that could potentially have
> other uses than my specific example above?
Your method should be called "apply!" because it's destructive. I'd
rather not do it that way, i.e. without changing the array at hand.You
can use inject for that.
I'd use #inject for your problem:
match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}
I wanted to write a simple method for comparing two paths on a Windows
system. My initial algorithm felt very contrived:
def same_path?(a, b)
a, b = [a, b].map{ |p| File.expand_path(p).downcase }
a == b
end
It felt like saving the result from #map and then doing the comparison
shouldn't be necessary. So I came up with the following solution:
class Array
def apply(method)
shift.send(method, *self)
end
end
This allowed me to define #same_path? thusly:
def same_path?(a, b)
[a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end
which, to me, looks a lot nicer. Array#apply takes a method (in a
symbolic manner) and applies it to its first element, passing the rest
of the elements as arguments to the given method.
Any thoughts? Is Array#apply a method that could potentially have
other uses than my specific example above?
Your method should be called "apply!" because it's destructive.
I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.
I think this makes sense. If unpaired dangerous methods have !, it
sort of suggests that any time there isn't a !, the method is
non-dangerous, which in turn suggests non-destructive... and that
isn't true.
It's not about the line count but about executing the same set of
functions/methods on both a and b. With the #inject or #apply
solution it's clear that both a and b undergo the same conversion.
nikolai
···
On Aug 14, 2:11 pm, Trans <transf...@gmail.com> wrote:
On Aug 14, 2:40 am, "nikolai.weib...@gmail.com"
> def same_path?(a, b)
> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> end
Your first solution is much easier to understand. In fact I would do:
def same_path?(a, b)
File.expand_path(a).downcase == File.expand_path(b).downcase
end
> I wanted to write a simple method for comparing two paths on a Windows
> system. My initial algorithm felt very contrived:
> def same_path?(a, b)
> a, b = [a, b].map{ |p| File.expand_path(p).downcase }
> a == b
> end
> It felt like saving the result from #map and then doing the comparison
> shouldn't be necessary. So I came up with the following solution:
> class Array
> def apply(method)
> shift.send(method, *self)
> end
> end
> This allowed me to define #same_path? thusly:
> def same_path?(a, b)
> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> end
> which, to me, looks a lot nicer. Array#apply takes a method (in a
> symbolic manner) and applies it to its first element, passing the rest
> of the elements as arguments to the given method.
> Any thoughts? Is Array#apply a method that could potentially have
> other uses than my specific example above?
Your method should be called "apply!" because it's destructive. I'd
rather not do it that way, i.e. without changing the array at hand.You
can use inject for that.
I'd use #inject for your problem:
match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}
Ugh. This was so NOT about whether to call the method Array#apply or
Array#apply!. I just wrote the absolute shortest solution I could
think of. I didn't suggest that it was the final version. Yes,
seeing as how my definition mutates its target, Array#apply! is the
appropriate name. I simply didn't want to clutter the definition
thusly:
class Array
def apply(method)
first.send(method, *self[1..-1])
end
end
(which sort of begs the question why we don't have Array#rest
nikolai
···
On Aug 14, 2:07 pm, dbl...@rubypal.com wrote:
Hi --
On Tue, 14 Aug 2007, Robert Klemme wrote:
> 2007/8/14, nikolai.weib...@gmail.com <nikolai.weib...@gmail.com>:
>> Hi!
>> I wanted to write a simple method for comparing two paths on a Windows
>> system. My initial algorithm felt very contrived:
>> def same_path?(a, b)
>> a, b = [a, b].map{ |p| File.expand_path(p).downcase }
>> a == b
>> end
>> It felt like saving the result from #map and then doing the comparison
>> shouldn't be necessary. So I came up with the following solution:
>> class Array
>> def apply(method)
>> shift.send(method, *self)
>> end
>> end
>> This allowed me to define #same_path? thusly:
>> def same_path?(a, b)
>> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
>> end
>> which, to me, looks a lot nicer. Array#apply takes a method (in a
>> symbolic manner) and applies it to its first element, passing the rest
>> of the elements as arguments to the given method.
>> Any thoughts? Is Array#apply a method that could potentially have
>> other uses than my specific example above?
> Your method should be called "apply!" because it's destructive.
I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.
Your method should be called "apply!" because it's destructive.
I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.
That is true for Array#delete. delete is always destructive, there is no
need to flag that.
But there is no need for your method to be destructive, so you need to
tell the world about it.
I think this makes sense. If unpaired dangerous methods have !, it
sort of suggests that any time there isn't a !, the method is
non-dangerous, which in turn suggests non-destructive... and that
isn't true.
It should simply be clear from the name[0] if a method is destructive.
Often you need the ! for that, in some cases it's obvious without the !.
def same_path?(a, b)
[a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end
Your first solution is much easier to understand. In fact I would do:
def same_path?(a, b)
File.expand_path(a).downcase == File.expand_path(b).downcase
end
It's not about the line count but about executing the same set of
functions/methods on both a and b. With the #inject or #apply
solution it's clear that both a and b undergo the same conversion.
perhaps you should just give the functions/methods a name:
def normalize path
File.expand_path(path).downcase
end
def same_path?(a, b)
normalize(a) == normalize(b)
end
Of course we are drifting far away from the topic of this thread.
More on topic: I would think an apply method would apply a function to each
member of the array (like map) - just from the sound of word.
nikolai
cheers
Simon
···
nikolai.weibull@gmail.com wrote:
On Aug 14, 2:11 pm, Trans <transf...@gmail.com> wrote:
> > I wanted to write a simple method for comparing two paths on a Windows
> > system. My initial algorithm felt very contrived:
> > def same_path?(a, b)
> > a, b = [a, b].map{ |p| File.expand_path(p).downcase }
> > a == b
> > end
> > It felt like saving the result from #map and then doing the comparison
> > shouldn't be necessary. So I came up with the following solution:
> > class Array
> > def apply(method)
> > shift.send(method, *self)
> > end
> > end
> > This allowed me to define #same_path? thusly:
> > def same_path?(a, b)
> > [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> > end
> > which, to me, looks a lot nicer. Array#apply takes a method (in a
> > symbolic manner) and applies it to its first element, passing the rest
> > of the elements as arguments to the given method.
> > Any thoughts? Is Array#apply a method that could potentially have
> > other uses than my specific example above?
> Your method should be called "apply!" because it's destructive. I'd
> rather not do it that way, i.e. without changing the array at hand.You
> can use inject for that.
> I'd use #inject for your problem:
> match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}
match = [a,b].map {|x| File.expand_path(x).downcase}.uniq.size == 1
Your method should be called "apply!" because it's destructive.
I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.
Ugh. This was so NOT about whether to call the method Array#apply or
Array#apply!.
I know -- I was just replying to a tangential point that Robert
raised.
I just wrote the absolute shortest solution I could
think of. I didn't suggest that it was the final version. Yes,
seeing as how my definition mutates its target, Array#apply! is the
appropriate name.
I would still disagree, on the grounds that ! doesn't mean that a
method changes its receiver; it means the method is "dangerous"
(Matz's definition), and that only has meaning in reference to a
non-dangerous but otherwise equivalent method.
I simply didn't want to clutter the definition
thusly:
class Array
def apply(method)
first.send(method, *self[1..-1])
end
end
(which sort of begs the question why we don't have Array#rest
Good question
David
···
On Tue, 14 Aug 2007, nikolai.weibull@gmail.com wrote:
Your method should be called "apply!" because it's destructive.
I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.
That is true for Array#delete. delete is always destructive, there is no
need to flag that.
And push, pop, <<, concat, replace, clear.... There's definitely no
general guideline that destructive methods end in !.
(Of course String has both delete and delete!
But there is no need for your method to be destructive, so you need to
tell the world about it.
I think this makes sense. If unpaired dangerous methods have !, it
sort of suggests that any time there isn't a !, the method is
non-dangerous, which in turn suggests non-destructive... and that
isn't true.
It should simply be clear from the name[0] if a method is destructive.
Often you need the ! for that, in some cases it's obvious without the !.
I guess I take my cue from the Ruby core/standard language, where
there's no use of !, as far as I know, except to distinguish a
"dangerous" method from its non-dangerous partner. I don't know of any
case where a method is just considered "dangerous" in the abstract,
without comparison to another method.
Basically, I can't find any object for which this:
obj.methods.grep(/!/).detect {|m| not obj.respond_to?(m.delete('!')) }
or similar tests with private methods, etc., returns a value.
Well, the documentation is pretty clear about what it does. Even
though I'd considered it bugged. Perhaps the semantics of Pathname#==
should be clarified before we post any change suggestions.
So, should Pathname#== respect the platforms case insensitivity, for
example, on Windows and on Mac OS?
Should Pathname#== try hard to make sure that both arguments are as
complete and clean as possible, for example, by calling
Pathname#expand_path on both arguments? This would have to take into
account that Pathname#== currently has taken any argument that
responds to #to_s.
nikolai
···
On Aug 14, 8:32 pm, Eric Hodel <drbr...@segment7.net> wrote:
On Aug 14, 2007, at 04:40, nikolai.weib...@gmail.com wrote:
> On Aug 14, 11:50 am, Eric Hodel <drbr...@segment7.net> wrote:
> > Is there something wrong with Pathname#==?
> Yes, not that it can't be fixed, but the current definition is sort of
> broken (on all systems):
Could you file a bug on the Ruby tracker? (Or has it been fixed?)
Ah, OK. Yeah, I suppose that's true. Still, my second suggestion for
how to implement Array#apply is probably better.
nikolai
···
On Aug 14, 4:38 pm, dbl...@rubypal.com wrote:
On Tue, 14 Aug 2007, nikolai.weib...@gmail.com wrote:
> On Aug 14, 2:07 pm, dbl...@rubypal.com wrote:
> Ugh. This was so NOT about whether to call the method Array#apply or
> Array#apply!.
I know -- I was just replying to a tangential point that Robert
raised.
> I just wrote the absolute shortest solution I could
> think of. I didn't suggest that it was the final version. Yes,
> seeing as how my definition mutates its target, Array#apply! is the
> appropriate name.
I would still disagree, on the grounds that ! doesn't mean that a
method changes its receiver; it means the method is "dangerous"
(Matz's definition), and that only has meaning in reference to a
non-dangerous but otherwise equivalent method.
I dislike this kind of automation behind the scenes because there may
be situations where you do not want this behavior. Also keep in mind
that ultimately a safe comparison needs to access the file system
which has a significant performance impact.
I'd prefer either a method like #normalize which does all the
necessary transformations which enable == to compare for identical
file system paths or add a method that does the comparison explicitly.
Maybe #realpath can be adjusted to deliver this.
On Aug 14, 8:32 pm, Eric Hodel <drbr...@segment7.net> wrote:
> On Aug 14, 2007, at 04:40, nikolai.weib...@gmail.com wrote:
> > On Aug 14, 11:50 am, Eric Hodel <drbr...@segment7.net> wrote:
> > > Is there something wrong with Pathname#==?
> > Yes, not that it can't be fixed, but the current definition is sort of
> > broken (on all systems):
> Could you file a bug on the Ruby tracker? (Or has it been fixed?)
Well, the documentation is pretty clear about what it does. Even
though I'd considered it bugged. Perhaps the semantics of Pathname#==
should be clarified before we post any change suggestions.
So, should Pathname#== respect the platforms case insensitivity, for
example, on Windows and on Mac OS?
Should Pathname#== try hard to make sure that both arguments are as
complete and clean as possible, for example, by calling
Pathname#expand_path on both arguments? This would have to take into
account that Pathname#== currently has taken any argument that
responds to #to_s.