|
You picked an extremely good built-in as an example. It doesn't function exactly as you've shown it, but there is very weird stuff going on. On the plus side, this is the only time I've seen something quite like this in Perl, so I'm not sure it's represents the state of using Perl as a whole very well. glob appears to do something special, and unlike most other Perl functions where it's just a matter of knowing the context and the API the function exposes. In the versions I just tested (including 5.21.6), glob in fact doesn't quite do what it's documentation says, in the apparent effort to "do what you mean". It does not iterate through files in the result when used in scalar context, it iterates through files in the result when used in scalar context and in the exact same call-site in the program. Here's all the files we'll test # perl -E 'my @files = glob("*"); say for @files;
one.a
one.b
two.a
two.b
Glob acts like an iterator when used in scalar context. Here, each call returns another file. # perl -E 'while ( my $file = glob("*") ) { say $file; } '
one.a
one.b
two.a
two.b
Glob gives the first file each time it's called in scalar context if it's not the same exact point in the source. This doesn't follow the docs, which says it will act like an iterator in scalar context. # perl -E 'my $file1 = glob("*"); my $file2 = glob("*"); say $file1; say $file2;'
one.a
one.a
Again, we see it's not acting like an iterator. Each call is generating it's own list and returning the first file. # perl -E 'my $file1 = glob("*"); my $file2 = glob("two*"); say $file1; say $file2;'
one.a
two.a
Here, we can see that since the same point in the code is getting hit, glob is acting like an iterator. # perl -E 'sub myglob { my $mask = shift; glob($mask); } my $file1 = myglob("*"); my $file2 = myglob("*"); say $file1; say $file2;'
one.a
one.b
Here we see that since the same point in code is getting hit, glob is ignoring it's input and just acting like an iterator on the first input, which seems to be what you were trying to show in your example. # perl -E 'sub myglob { my $mask = shift; glob($mask); } my $file1 = myglob("*"); my $file2 = myglob("two*"); say $file1; say $file2;'
one.a
one.b
That is very weird behavior. I agree it's not consistent with the rest of the language. I'm not sure it's indicative of the language as a whole though, as it appears to be due to weird historical implementation details that are kept for backwards compatibility. The perldoc for glob says it's implemented using the the standard File::Glob module. The perldoc for File::Glob mentions that it implements the code glob in terms of bsd_glob (the FreeBSD glob(3) routine, a superset of POSIX glob), which is function that can also be exported. In fact, the bsd_glob function, when used, acts as we would wish, without weird iterator behavior (without iterator behavior at all, in fact). # perl -E 'use File::Glob qw/:bsd_glob/; my $file1 = bsd_glob("*"); my $file2 = bsd_glob("two*"); say $file1; say $file2;'
two.b
two.b
# perl -E 'use File::Glob qw/:bsd_glob/; sub myglob { my $mask = shift; bsd_glob($mask); } my $file1 = myglob("*"); my $file2 = myglob("one*"); say $file1; say $file2;'
two.b
one.b
Of course, this means the documentation that says the core glob routine is implemented using bsd_glob has some glaring omissions.So, congratulations, you picked an extremely good example and unearthed some crazy Perl arcana, and possibly a bug (there's some open, longstanding tickets regarding glob bugs[1][2], which seem to boil down to "we're doing what we can to make it better, but we're hampered by backwards compatibility and weird semantics"). I think the documentation is woefully inadequate to explain what's going on in this case though. Note: that bsd_glob seems to return the last item when used in scalar context, not the first. [1]: https://rt.perl.org/Ticket/Display.html?id=2707
[2]: https://rt.perl.org/Ticket/Display.html?id=2713
|