in reply to create array of empty files and then match filenames

Can I also ask one more thing that I will need later? I was just trying to sketch on a paper how it would go but didn't manage to do it. If I have two files in my directory, the same way I described, that have only numbers in their filenames, for example 8 and 9, this line

my @emptyfiles = grep { /^\d+$/ } grep { -f } glob '*';

would match both of them. How could I match them separately? As in, put the "biggest number" in a variable and the "biggest number minus 1" in another variable?

I tried this:
my $emptyfile; #structure file, new. my $previous; #structure file, alternative conformation. my @emptyfiles = grep { /^\d+$/ } grep { -f } glob '*'; $previous = ($emptyfile)-1); #set that $previous is the previous alter +native conformation. #print "previous is $previous \n"; #push (@emptyfiles, $emptyfile); #push (@emptyfiles, $previous); foreach (@emptyfiles) { if (-z "$emptyfile") { print "found previous conformation: $previous \n"; print "found new conformation: $emptyfile \n"; } # if -z loop. } #foreach loop.

But this obviously doesn't work. It keeps saying that $previous is -1, although in my example I have used files by the number of 8 and 9. So it should say yes, I found 9 and then I found 8.

When I use the push command again it doesn't work, so I obviously don't know how to use it and have to do some homework.

I found this http://stackoverflow.com/questions/10701210/how-to-find-maximum-and-minimum-value-in-an-array-of-integers-in-perl that mentions function "List::Util", does that sound correct for my purpose?"

Also sorry for the extensive commenting on my script, I need it so that I won't get lost.

Replies are listed 'Best First'.
Re^2: create array of empty files and then match filenames
by poj (Abbot) on Jan 07, 2016 at 18:12 UTC

    You probably want something like this

    #!/usr/bin/perl use strict; my @emptyfiles = grep { /^\d+$/ } grep { -f } glob '*'; foreach my $emptyfile (@emptyfiles) { my $previous = $emptyfile - 1; if ((-z $emptyfile) and (-f $previous)) { print "found previous conformation: $previous \n"; print "found new conformation: $emptyfile \n"; } # if -z loop. } #foreach loop.

    Have a look at scalars and arrays in Perl variable types.

    poj
      I wish I had seen this earlier! I tried "and" but I got compilation errors (again one more mistake lol) so in the end I did this:
      use List::Util qw( min max ); my @numbers; @numbers = grep { /^\d+$/ } grep { -f } glob '*'; my $max = max@numbers; my $previous = ($max)-1; foreach (@numbers) { if (-z "$max") { if (-z "$previous") { print "previous conf: $previous \n"; print "Latest conf: $max \n"; printf "Ready for conf %d \n",++$max; } } }
      Now I need to think about "else" messages/actions. Sorry, I changed my array name halfway through the thread, I just used directly the "@numbers" example from List::Util that I found at stack overflow

        Do you need a foreach loop ?.
        You are checking the same value of $previous regardless of the loop variable $_

        poj
Re^2: create array of empty files and then match filenames
by kcott (Archbishop) on Jan 07, 2016 at 18:50 UTC

    If you numerically sort your filenames in descending order, you will have the biggest in the first element (index 0) and the next biggest in the next element.

    Modifying the code I provided earlier:

    #!/usr/bin/perl -l use strict; use warnings; use autodie; my $dir = '.'; opendir(my $dh, $dir); my @emptyfiles = sort { $b <=> $a } grep { -f && /^\d+$/ && -z } readd +ir $dh; closedir $dh; print for @emptyfiles; print "Biggest: $emptyfiles[0]"; print "Next biggest: $emptyfiles[1]";

    Gives this output:

    123 1 Biggest: 123 Next biggest: 1

    So, they're effectively in separate variables already (i.e. $emptyfiles[0] and $emptyfiles[1]). There's probably no need to use other variables but, if you really need to, just do something like this (untested):

    my ($biggest, $next_biggest) = @emptyfiles[0, 1];

    Regarding your problem with "It keeps saying that $previous is -1", there's at least two problems. Firstly, the code you posted

    $previous = ($emptyfile)-1);

    won't compile. You'll get something like: "syntax error at ... line ..., near "1)""

    So, the code you posted isn't the code you're running; however, given you don't initialise $emptyfile, subtracting 1 from it will evaluate to -1 and should also give you a warning (assuming you still have use warnings; at the top of your code). Here's a minimal example:

    $ perl -wE 'my $x; say($x - 1)' Use of uninitialized value $x in subtraction (-) at -e line 1. -1

    — Ken

      Hi! I tried the "sort" function for a different thing I wanted to do, but it doesn't work as expected.
      my @confs = (); push (@confs, $confs); my @sortedconfs; @sortedconfs = sort {$a cmp $b} @confs; print @sortedconfs; print "\n";

      So I put all my conformations ($confs) in an array (@confs) and then I want to sort them numerically. However the output I get is only one file, the biggest number one, not a row of numbers sorted numerically. What do you think might be going wrong?

      I'm also concerned by the fact that if I do "sort {$b cmp $a}" I get the same result as with {$a cmp $b}, shouldn't I get the reversed order? The variable $confs is my numbered chemical conformations, which is always 4 characters long in the style of B001, B002, B003 and so on. This variable is in my filenames along with other similar ones (similar to a filename like MOLEC1-B001-OPT-FREQ2), so I want to sort my files, according to their chemical conformations, numerically. But if I do "sort {$a <=> $b}" it complains that "it's not a numerical value", that's why I tried with cmp as seen here http://www.perlmonks.org/?node_id=259465. Any thoughts?

      edit: I read a bit more and found that in mixed strings I should extract the numerical value and sort the substring, so I changed the previous "sort" line with "{ substr($a, 1) <=> substr($b, 1) }" but I get the same output :(

        The short answer is, with the code you've posted, you only populate @confs with one element (i.e. $confs) so there is nothing to sort!

        When posting a question, you need to show us:

        • Enough of your code such that we can reproduce the problem.
        • Your input data (which should be a small, representative sample).
        • Your actual output.
        • Your expected output.
        • All error or warning messages you receive.

        Input and output (including any messages) needs to be shown to us exactly as you see it, i.e. not prosaic descriptions of the same. All of this, and more, is explained in the "How do I post a question effectively?" guidelines: please read them.

        In Re^4: create array of empty files and then match filenames, poj asked "What is in $confs?". You didn't really answer this question. Is it a series of Bnnn substrings, a series of filenames, or something else? A simple:

        my $confs = '...';

        in your code would have probably told us all we need to know.

        From what you've posted here, and elsewhere in this thread, I'm wondering whether (as part of your learning) you haven't really understood how to populate arrays. Consider these two lines of code:

        my @array1 = (1, 2, 3); my @array2 = '1, 2, 3';

        @array1 contains three elements: the digits 1, 2 and 3. @array2 contains one element: the string '1, 2, 3'.

        This also works the same for push which you seem to have used (probably incorrectly) in a number of places. Consider this code which results in @array1 and @array2 being populated exactly the same as in the last example:

        my (@array1, @array2); my $elements_for_array = '1, 2, 3'; push @array1, 1, 2, 3; # @array1 has three elements push @array2, $elements_for_array; # @array2 has one element

        The "push @array2, $elements_for_array;" example seems to be what you've done a few times and maybe has caused you problems. I'm really just guessing but perhaps that helps. See also "perlintro: Perl variable types" and "perldata: List value constructors".

        In an attempt to get you back on track with your code, here's my guess as to the type of thing you need:

        #!/usr/bin/env perl -l use strict; use warnings; my @filenames = qw{ MOLEC8-B040-OPT-FREQ2.gout MOLEC1-B001-OPT-FREQ2.gout MOLEC2-B010-OPT-FREQ2.gout MOLEC10-B002-OPT-FREQ2.gout }; my $re = qr{^[^-]+-([^-]+)}; my @sorted = sort { ($b =~ /$re/)[0] cmp ($a =~ /$re/)[0] } @filenames +; print 'Original filenames:'; print for @filenames; print 'Sorted filenames:'; print for @sorted;

        Possibly the only tricky bits in that code are they parts like "($x =~ /$re/)[0]". $re contains one capture, i.e. ([^-]+), which gets the Bnnn part of each filename. In list context, the regex match evaluates to a list of the captures; as there's only one, ($x =~ /$re/) evaluates to something like ('Bnnn'). The zeroth index into that list evaluates to 'Bnnn' which provides a string for the cmp operation. [Also note that the regex I've used allows MOLECn to go above MOLEC9; the MOLEC10 example successfully tests this.]

        Here's the output from running that script:

        Original filenames: MOLEC8-B040-OPT-FREQ2.gout MOLEC1-B001-OPT-FREQ2.gout MOLEC2-B010-OPT-FREQ2.gout MOLEC10-B002-OPT-FREQ2.gout Sorted filenames: MOLEC8-B040-OPT-FREQ2.gout MOLEC2-B010-OPT-FREQ2.gout MOLEC10-B002-OPT-FREQ2.gout MOLEC1-B001-OPT-FREQ2.gout

        — Ken

        What is in $confs ?

        I'm guessing you need to use split to create @confs

        #!perl use strict; my $confs = 'B001, B003, B002'; my @confs = split /, /,$confs; my @sortedconfs = sort {$a cmp $b} @confs; print "@sortedconfs\n";
        poj