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§.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§.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?
nikolai
On Aug 14, 2007, at 02:40, [email protected] wrote:
I wanted to write a simple method for comparing two paths on a Windows
system.
Is there something wrong with Pathname#==?
On Aug 14, 11:50 am, Eric H. [email protected] wrote:
On Aug 14, 2007, at 02:40, [email protected] wrote:
I wanted to write a simple method for comparing two paths on a Windows
system.
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):
Compare this pathname with +other+. The comparison is string-
based.
Be aware that two different paths (foo.txt and ./
foo.txt)
can refer to the same file.
def ==(other)
return false unless Pathname === other
other.to_s == @path
end
alias === ==
alias eql? ==
nikolai
2007/8/14, [email protected] [email protected]:
It felt like saving the result from #map and then doing the comparison
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).
Kind regards
robert
Hi –
On Tue, 14 Aug 2007, Robert K. 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.
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.
David
On Aug 14, 1:51 pm, “Robert K.” [email protected]
wrote:
a, b = [a, b].map{ |p| File.expand_path(p).downcase }
end
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}
Ah, I had no idea inject would take the first item of an array if not
given an argument; thanks! That solves my problem.
nikolai
On Aug 14, 2:40 am, “[email protected]”
[email protected] wrote:
It felt like saving the result from #map and then doing the comparison
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)
a = File.expand_path(a).downcase
b = File.expand_path(b).downcase
a == b
end
It’s very clear and actually should be a tad faster. LOC isn’t
everything!
But if you just can’t abide by more that one line:
def same_path?(a, b)
File.expand_path(a).downcase == File.expand_path(b).downcase
end
HTH,
T.
On Aug 14, 1:51 pm, “Robert K.” [email protected]
wrote:
Yet another solution is to use one of Pathname’s multiple methods (for
example #realpath).
The problem is that Pathname’s comparison is case sensitive.
nikolai
On Aug 14, 2:11 pm, Trans [email protected] wrote:
On Aug 14, 2:40 am, “[email protected]”
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.
nikolai
On Tue, 14 Aug 2007, [email protected] wrote:
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
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 !.
mfg, simon … [0] is that actually english?
On Aug 14, 2007, at 04:40, [email protected] wrote:
broken (on all systems):
Could you file a bug on the Ruby tracker? (Or has it been fixed?)
On Aug 14, 2:07 pm, [email protected] wrote:
shift.send(method, *self)
symbolic manner) and applies it to its first element, passing the rest
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 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
[email protected] wrote:
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
On Aug 14, 8:32 pm, Eric H. [email protected] wrote:
On Aug 14, 2007, at 04:40, [email protected] wrote:
On Aug 14, 11:50 am, Eric H. [email protected] 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.
nikolai
2007/8/15, [email protected] [email protected]:
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.
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.
Just my 0.02EUR…
Kind regards
robert
On Aug 14, 2007, at 11:44 PM, [email protected] wrote:
So, should Pathname#== respect the platforms case insensitivity, for
example, on Windows and on Mac OS?
Seems like a bad idea to me. Case sensitivity is not a property of
the platform; it is a property of a given file system. It’s easy to
have some case sensitive and some case insensitive file systems on
many platforms, including Mac OS and Windows.
Besides, there is more to pathname equivalence than case sensitivity.
For example, some older file systems defined case insensitivity based
on the letters A through Z, completely ignoring other characters.
Worse, they often just assumed 0x41 == 0x61, even for multi-byte
character encodings (meaning two unrelated characters were considered
equal). Some file systems take diacriticals into account, and some
don’t.
In the end, you generally have to ask the file system to look up the
path and return some unique identifier (such as a combination of
device identifier and node number). Then you can (usually) compare
those unique identifiers. That only works for paths that already
exist. Predicting whether a non-existant path will be equivalent to
some other path is hard.
-Mark
On Aug 15, 8:44 pm, William J. [email protected] wrote:
system. My initial algorithm felt very contrived:
def apply(method)
which, to me, looks a lot nicer. Array#apply takes a method (in a
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
Very nice.
nikolai
On Aug 14, 4:38 pm, [email protected] wrote:
On Tue, 14 Aug 2007, [email protected] wrote:
On Aug 14, 2:07 pm, [email protected] 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.
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, 6:51 am, “Robert K.” [email protected]
wrote:
end
This allowed me to define #same_path? thusly:
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