CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
SCALAR Function

 



sriharsha_12
Novice

Feb 10, 2016, 8:55 PM

Post #1 of 7 (4486 views)
SCALAR Function Can't Post

I created a directory 'a' with 3 sub directories 'a1', 'a2' & 'a3'. Each sub directory has a file 'date.txt'. Similarly 'b', 'c', 'd', 'e' & 'f'.

The following code,


Code
#!/tools/cfr/perl/5.14.1/bin/perl 

use feature 'say';

my @a = ('a', 'b', 'c', 'd', 'e', 'f');

foreach my $f (@a)
{
say "Reading directory '$f'...";
say 'List content:';
say <$f/*>;
say 'Scalar content:';
say scalar <$f/*>;
say '************************';
}

exit;


generates,

Code
Reading directory 'a'... 
List content:
a/a1a/a2a/a3
Scalar content:
a/a1
************************
Reading directory 'b'...
List content:
b/b1b/b2b/b3
Scalar content:
a/a2
************************
Reading directory 'c'...
List content:
c/c1c/c2c/c3
Scalar content:
a/a3
************************
Reading directory 'd'...
List content:
d/d1d/d2d/d3
Scalar content:

************************
Reading directory 'e'...
List content:
e/e1e/e2e/e3
Scalar content:
e/e1
************************
Reading directory 'f'...
List content:
f/f1f/f2f/f3
Scalar content:
e/e2
************************


Why there is no scalar value returned for directory 'd'?

Scalar content of 'b' should have been 'b/b1' but it shows 'a/a2'. Similarly 'c', 'e' & 'f' show wrong content.

What is the mistake in my code?


(This post was edited by sriharsha_12 on Feb 11, 2016, 2:05 AM)


Laurent_R
Veteran / Moderator

Feb 11, 2016, 2:58 AM

Post #2 of 7 (4474 views)
Re: [sriharsha_12] SCALAR Function [In reply to] Can't Post

Well, maybe the first question to be asked is why, when reading directory "b", you get "a/a2", and so on.

The problem, when you do that:

Code
say scalar <$f/*>;

is that you're reading the iterator in scalar context, meaning that you retrieve only the first entry provided by the iterator, so that the iterator will give you the next piece of data next time you call it.

It seems that you're really are getting two separate iterators, one working in list context and one in scalar context, and I don't really know why you're getting two.

But this is clearly not a good way to call these iterator. You should probably call the iterator only once per iteration, store the output in a variable (an array) and then process the content os this variable.

The real question is: why are you doing something so strange? Why are you calling the iterator a second time within the same loop with the scalar keyword? What do you expect to obtain?


BillKSmith
Veteran

Feb 11, 2016, 1:51 PM

Post #3 of 7 (4457 views)
Re: [sriharsha_12] SCALAR Function [In reply to] Can't Post

Reading a directory in scalar context does not do exactly what you expect. The strange behavior is correctly documented in perldoc perlop.

Quote
A (file)glob evaluates its (embedded) argument only when it is starting a new list. All values must be read before it will start over. In list context, this isn't important because you automatically get them all anyway. However, in scalar context the operator returns the next value each time it's called, or undef when the list has run out.


In your case, the first call works as expected. The next two ignore their arguments and return the next two a's. The next call <"d"> ignores its argument and returns undef to indicate "the list has run out". (Your failure to use warnings hid the error message.) Your next call <"e"> works as expected because you are starting over. The last call ignores its argument and returns the second e.

I agree with Laurent that the best practical fix is to read the list output into an array and then process the array.
Good Luck,
Bill


sriharsha_12
Novice

Feb 11, 2016, 11:28 PM

Post #4 of 7 (4442 views)
Re: [BillKSmith] SCALAR Function [In reply to] Can't Post

Thanks guys.

I was testing the glob<> & scalar functionality and I shared the same code.

Actually I'm trying to verify the given list of directories are not empty. My real code looks something like this,


Code
foreach my $f (@dir_list) 
{
say "Reading directory '$f'...";
unless (scalar <$f/*>)
{
say "Directory $f is empty"; # print_error ("Directory $f is empty");
}
say '************************';
}

Output (For the above example),
Reading directory 'a'...
************************
Reading directory 'b'...
************************
Reading directory 'c'...
************************
Reading directory 'd'...
Directory d is empty
************************
Reading directory 'e'...
************************
Reading directory 'f'...
************************


Since the $f value is changed in the next iteration, I thought <$f/*> will return the new list. But looks like file glob<> is maintaining the list from the previous call.

JFYI, without reading the output of file glob<> into an array, is there any other way to reset it?


BillKSmith
Veteran

Feb 12, 2016, 6:06 AM

Post #5 of 7 (4426 views)
Re: [sriharsha_12] SCALAR Function [In reply to] Can't Post

The best way to accomplish your goal is to read the directory in list context and then test that the list is not empty.


Code
   my @file_names = <$f/*>; 
unless (@file_names) {


It is possible (but tricky) to combine these two statements. I would not recommend it. Remember that it is necessary to read in list context and then test the result in scalar context.

I am not certain, but the quote in my previous post seems to imply that it is not possible to reset the scalar context sequence. Fortunately, this does not affect your problem.
Good Luck,
Bill


FishMonger
Veteran / Moderator

Feb 12, 2016, 7:16 AM

Post #6 of 7 (4420 views)
Re: [sriharsha_12] SCALAR Function [In reply to] Can't Post

If you only need the count of files and not the actual list of filenames, then I'd do it this way:

Code
foreach my $f (@dir_list) { 
if (my $cnt = () = <$f/*>) {
say '************************';
}
else {
say "Directory $f is empty";
}
}


If you don't like putting it in the conditional like that, you could split it out.

Code
    my $cnt = () = <$f/*>; 
if ($cnt) {



sriharsha_12
Novice

Feb 14, 2016, 9:20 PM

Post #7 of 7 (4372 views)
Re: [FishMonger] SCALAR Function [In reply to] Can't Post

Thanx guys. I'll change the code as suggested.

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives