http://qs321.pair.com?node_id=289713

AcidHawk has asked for the wisdom of the Perl Monks concerning the following question:

A thousand pardon for this silly question (been away for a while)

I need to read in a list of numbers and make sure that the numbers start at 1 and increment by 1 until the end of the list. So...

  • 1 2 3 4 5 would return a success.
  • 3 2 1 4 5 would return a success.
  • 2 3 1 1 6 would return a failure
  • 1 2 4 5 6 would return a failure
  • I have the following code (which works) and was wondering if there is another way of doing this.. I could not find a module on CPAN after a little search earlier today.

    #! /usr/bin/perl use strict; use warnings; print "Please enter a list of numbers (I.e. 1 2 3 4 5)\n"; chomp(my $n = <STDIN>); my @numlist = split/\s/, $n; @numlist = sort { $a <=> $b } @numlist; if ($numlist[0] == 1) { print "Horray Array starts at 1\n"; my $prevnum = 0; foreach my $number (@numlist) { unless (($prevnum + 1) == $number) { print "Error in Sequence"; $prevnum = 0; last; } else { $prevnum = $number; } } if ($prevnum) { print "The numbers are correct\n"; } } else { print "Error Array DOES NOT Start at 1\n"; }

    P.S. the verbose code is to try and help with readability

    -----
    Of all the things I've lost in my life, its my mind I miss the most.

    Replies are listed 'Best First'.
    Re: Validate array of numbers in sequence
    by matsmats (Monk) on Sep 08, 2003 at 09:17 UTC

      Since you obviously already has solved the problem, I guess you're mostly after other ways to do it just out of novelty or something. But here's another way, at least:

      my @numlist = (1,2,3,4,5); if (join (",", @numlist ) eq join (",", (1..$#numlist+1))) { print "correct"; } else { print "incorrect"; }

      Or more onelinerish:

      print ( (join (",", @numlist) eq join (",", (1..$#numlist+1))) ? "corr +ect" : "incorrect" );

      Seems like Array::Compare could be a good place to go too.

      Mats
    Re: Validate array of numbers in sequence
    by wirrwarr (Monk) on Sep 08, 2003 at 09:15 UTC
      Your solution for the problem is fine, I think. But I have two remarks on the code:
      You check twice for the "1" at index 0.
      Your loop is somewhat complicated. Try something like:
      my $index = 1; foreach my $number (@numlist) { unless ($index == $number) { print "..."; last; } $index ++; }
      daniel.
    Re: Validate array of numbers in sequence
    by Abigail-II (Bishop) on Sep 08, 2003 at 09:44 UTC
      I wouldn't sort them.

      my @numlist = ....; my %numbers; @numbers {@numlist} = (); die "Not valid\n" unless @numlist == keys %numbers; exists $numbers {$_} or die "Not valid\n" for 1 .. @numlist; print "Valid\n"; __END__

      Abigail

    Re: Validate array of numbers in sequence
    by broquaint (Abbot) on Sep 08, 2003 at 09:37 UTC
      Here's my lazy attempt
      use strict; ## I've lazily pre-sorted these ;) my @ars = ( [ qw/ 1 2 3 4 5 / ], [ qw/ 1 2 3 4 6 / ], [ qw/ 2 3 4 6 7 / ], ); for(@ars) { print "broken list: @$_\n" and next if $_->[0] != 1; my($i, $n) = 0; $n = $_->[$i] while $i <= $#{$_} and $_->[$i] + 1 == $_->[++$i]; print "woo: @$_\n" if $n == $_->[-1]; print "doh: sequence broke at index ${\($i - 1)} (@$_[$i - 1,$i])\n" if $n != $_->[-1]; } __output__ woo: 1 2 3 4 5 doh: sequence broke at index 3 (4 6) broken list: 2 3 4 6 7
      So basically it checks the sequence by iterating through the pre-sorted array until there's a gap in the sequence. Then just checks whether the end value is the same as last value seen in the iteration.
      HTH

      _________
      broquaint

    Re: Validate array of numbers in sequence
    by cLive ;-) (Prior) on Sep 08, 2003 at 09:41 UTC
      timtowtdi :)
      #!/usr/bin/perl use strict; use warnings; my @list = qw( 1 3 5 4 2 6 7 8 9); my %hash; @hash{@list}=1; delete $hash{$_} for (1..@list); print "is sequence\n" unless %hash;

      cLive ;-)

        Both, your and Abigail-IIs solution will fail for duplicate elements (although I don't know whether that's a problem or not) :

        #!/usr/bin/perl use strict; use warnings; my @list = qw( 1 1 2 ); my %hash; @hash{@list}=1; delete $hash{$_} for (1..@list); print "is sequence\n" unless %hash;
        perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
          Fair point. Just switch things around a little...
          my %hash; @hash{1..@list}=1; delete $hash{$_} for (@list);

          Funny, this was how I did it originally, but I thought the other way round looked prettier :)

          cLive ;-)

        Thanks for the input but this does not return the correct results.

        E.g. If I have an array with 1 2 2 2 2 2 2 2 9 your code returns is sequence

        -----
        Of all the things I've lost in my life, its my mind I miss the most.
    Re: Validate array of numbers in sequence
    by DrHyde (Prior) on Sep 08, 2003 at 10:25 UTC
      Seeing that you're looking for other ways to do it, I offer this, which solves the general case of validating any sequence where the value of Nn depends only on Nn-1:
      # first the sequences to test my @sequences = ( [qw(1 2 3 4 5)], # start at 1, inc by 1 [qw(1 3 5 7 9)], # start at 1, inc by 2 [qw(0 1 0 1 0)], # start at 0, alternate 0/1 [qw(2 1 0 -1 -2)] # start at 2, dec by 1 ); # the functions to test them against - each one should # match just one of the sequences above my @functions =( { text => 'start at 1, increment by 1', start => 1, function => sub { $_[0] + 1 } }, { text => 'start at 1, increment by 2', start => 1, function => sub { $_[0] + 2 } }, { text => 'start at 0, alternate 0/1', start => 0, function => sub { ($_[0] == 0) ? 1 : 0 } }, { text => 'start at 2, decrement by 1', start => 2, function => sub { $_[0] - 1 } } ); foreach my $function (@functions) { print $function->{text}."\n"; foreach my $sequence (@sequences) { print ' '; print 'not ' unless( checksequence( @{$sequence}, $function->{start}, $function->{function} ) ); print 'ok ['.join(', ', @{$sequence})."]\n"; } } # takes a sequence, the desired start value, and the # function to generate the next correct value sub checksequence { # the sequence is the most important parameter, so # i pass it first. my($f, $z, @seq) = reverse(@_); @seq = reverse(@seq); # check that the first value is correct return 0 unless(shift(@seq) == $z); # now see if any subsequent values are wrong and # if they are, return immediately foreach my $value (@seq) { return 0 unless(($z = $f->($z)) == $value); } 1; }
      Of course, with some devious and evil trickery the function you pass in to checksequence() could maintain some state information, so it could check more complicated sequences where a value depends on more than just its immediate predecessor.
    Re: Validate array of numbers in sequence
    by Roger (Parson) on Sep 08, 2003 at 12:10 UTC
      The following is my little implementation:
      #!/usr/bin/perl use strict; my @list1 = qw/ 1 2 3 4 5 6 /; my @list2 = qw/ 6 2 1 5 4 3 /; my @list3 = qw/ 7 1 6 5 4 3 /; my @list4 = qw/ 4 5 7 6 2 3 /; my @list5 = qw/ 0 4 5 6 2 3 /; printf "%s\n", validate_number_list (\@list1) ? "Ok" : "Error"; printf "%s\n", validate_number_list (\@list2) ? "Ok" : "Error"; printf "%s\n", validate_number_list (\@list3) ? "Ok" : "Error"; printf "%s\n", validate_number_list (\@list4) ? "Ok" : "Error"; printf "%s\n", validate_number_list (\@list5) ? "Ok" : "Error"; sub validate_number_list() { my ($list) = @_; my @sorted_list = sort { $a <=> $b } @{$list}; my $prev; foreach (@sorted_list) { $prev + 1 != $_ ? return(0) : {$prev = $_}; } return 1; }
      The above code will give the following result:

      Ok
      Ok
      Error
      Error
      Error

      Which is what we expect.
    Re: Validate array of numbers in sequence
    by johndageek (Hermit) on Sep 08, 2003 at 20:52 UTC
      Another way for fun

      Dageek

      #!/usr/bin/perl -w use strict; my @list = ( 1,4,3, 2, 5, 6); my $index=0; ## set to the number your list should start at my $list_start=1; @list = sort {$a<=>$b} @list; my $msg = "list is ok\n"; for (@list) { if ($list[$index] != $index + $list_start) { $msg="bad list\n"; last; } $index++; } print "$msg";
    Re: Validate array of numbers in sequence
    by dtr (Scribe) on Sep 08, 2003 at 20:00 UTC

      An even simpler way to do this would be to add all of the numbers up. This also has the performance benefit of avoiding the sort entirely.

      For example:-

      my $total = 0; my $expect = 0; for(int i=0;i<@array;i++) { $total += $array[$i]; $expect += $i; Correction: $expect += $i + 1; } return 1 if ($total == $expect);

      Aaarrrggghhh - sorry, ignore this post. As has been pointed out, it doesn't work.

        This method does not work!

        You can not validate the number sequence by simply adding it up. Consider the following lists:

        1, 1, 4, 4 and 1, 2, 3, 4. Both add up to 10, but the first list is certainly not in sequence!