Adjacent number algorithm grouper

前端 未结 13 2077
不思量自难忘°
不思量自难忘° 2021-02-15 06:55

By which I mean this:

Given the input set of numbers:

1,2,3,4,5 becomes \"1-5\".

1,2,3,5,7,9,10,11,12,14 becomes \"1-3, 5, 7, 9-12, 14\"

This is

13条回答
  •  萌比男神i
    2021-02-15 07:46

    Perl

    With input validation/pre-sorting

    You can easily get the result as a LoL if you need to do something more fancy than just return a string.

    #!/usr/bin/perl -w
    
    use strict;
    use warnings;
    
    use Scalar::Util qw/looks_like_number/;
    
    sub adjacenify {
        my @input = @_;  
    
        # Validate and sort
        looks_like_number $_ or
            die "Saw '$_' which doesn't look like a number" for @input;
        @input = sort { $a <=> $b } @input;
    
        my (@output, @range);
        @range = (shift @input);
        for (@input) {
            if ($_ - $range[-1] <= 1) {
                push @range, $_ unless $range[-1] == $_; # Prevent repetition
            }
            else {
                push @output, [ @range ];
                @range = ($_); 
            }
        }   
        push @output, [ @range ] if @range;
    
        # Return the result as a string. If a sequence is size 1, then it's just that number.
        # Otherwise, it's the first and last number joined by '-'
        return join ', ', map { 1 == @$_ ? @$_ : join ' - ', $_->[0], $_->[-1] } @output;
    }
    
    print adjacenify( qw/1 2 3 5 7 9 10 11 12 14/ ), "\n";
    print adjacenify( 1 .. 5 ), "\n";
    print adjacenify( qw/-10 -9 -8 -1 0 1 2 3 5 7 9 10 11 12 14/ ), "\n";
    print adjacenify( qw/1 2 4 5 6 7 100 101/), "\n";
    print adjacenify( qw/1 62/), "\n";
    print adjacenify( qw/1/), "\n";
    print adjacenify( qw/1 2/), "\n";
    print adjacenify( qw/1 62 63/), "\n";
    print adjacenify( qw/-2 0 0 2/), "\n";
    print adjacenify( qw/-2 0 0 1/), "\n";
    print adjacenify( qw/-2 0 0 1 2/), "\n";
    

    Output:

    1 - 3, 5, 7, 9 - 12, 14
    1 - 5
    -10 - -8, -1 - 3, 5, 7, 9 - 12, 14
    1 - 2, 4 - 7, 100 - 101
    1, 62
    1
    1 - 2
    1, 62 - 63
    -2, 0, 2
    -2, 0 - 1
    -2, 0 - 2
    -2, 0 - 2
    

    And a nice recursive solution:

    sub _recursive_adjacenify($$);
    sub _recursive_adjacenify($$) {
        my ($input, $range) = @_;
    
        return $range if ! @$input;
    
        my $number = shift @$input;
    
        if ($number - $range->[-1] <= 1) {
            return _recursive_adjacenify $input, [ @$range, $number ];
        }
        else {
            return $range, _recursive_adjacenify $input, [ $number ];
        }
    }
    
    sub recursive_adjacenify {
        my @input = @_;
    
        # Validate and sort
        looks_like_number $_ or
            die "Saw '$_' which doesn't look like a number" for @input;
        @input = sort { $a <=> $b } @input;
    
        my @output = _recursive_adjacenify \@input, [ shift @input ];
    
        # Return the result as a string. If a sequence is size 1, 
        # then it's just that number.
        # Otherwise, it's the first and last number joined by '-'
        return join ', ', map { 2 == @$_ && $_->[0] == $_->[1] ? $_->[0] : 
                                1 == @$_ ? @$_ : 
                                join ' - ', $_->[0], $_->[-1] } @output;
    
    }
    

提交回复
热议问题