#!/usr/bin/perl
=encoding utf8
=begin metadata
Name: seq
Description: print a numeric sequence
Author: Michael Mikonos
License: artistic2
=end metadata
=cut
use strict;
use File::Basename qw(basename);
use POSIX qw(floor);
use constant EX_SUCCESS => 0;
use constant EX_FAILURE => 1;
my $Program = basename($0);
my $begin = 1;
my $step = 1;
my $ender;
my $format = "%g";
my $term = "\n";
sub usage {
warn "usage: $Program [-f format] [-s string] [begin [step]] end\n";
exit EX_FAILURE;
}
while (@ARGV && $ARGV[0] =~ /^-/) {
my $opt = shift;
if ($opt eq '--') {
last;
} elsif ($opt eq '-s') {
$term = shift;
} elsif ($opt eq '-f') {
$format = shift;
} elsif ($opt =~ m/\A\-?[0-9]/) {
unshift @ARGV, $opt;
last;
} else {
warn "$Program: unexpected option: '$opt'\n";
usage();
}
}
if (@ARGV == 0) {
usage();
} elsif (@ARGV == 1) {
$ender = getnum($ARGV[0]);
} elsif (@ARGV == 2) {
$begin = getnum($ARGV[0]);
$ender = getnum($ARGV[1]);
} elsif (@ARGV == 3) {
$begin = getnum($ARGV[0]);
$step = getnum($ARGV[1]);
$ender = getnum($ARGV[2]);
} else {
warn "$Program: extra argument '$ARGV[3]'\n";
usage();
}
if ($step == 0) {
warn "$Program: illegal step value of zero\n";
exit EX_FAILURE;
}
if ($ender < $begin) {
if (@ARGV != 3) {
$step = -$step;
} elsif ($step > 0) {
warn "$Program: needs negative decrement\n";
exit EX_FAILURE;
}
} else {
if ($step < 0) {
warn "$Program: needs positive increment\n";
exit EX_FAILURE;
}
}
my $head = 1;
my $count = floor(($ender - $begin) / $step);
for (0 .. $count) {
if ($head) {
$head = 0;
} else {
print $term;
}
printf $format, $begin + $_ * $step;
}
print "\n";
exit EX_SUCCESS;
sub getnum {
my $n = shift;
if ($n !~ m/\A[\+\-]?[0-9]+(\.[0-9]+)?\Z/) {
warn "$Program: invalid number '$n'\n";
exit EX_FAILURE;
}
return $n;
}
__END__
=head1 NAME
seq - print a numeric sequence
=head1 SYNOPSIS
seq [OPTIONS] LAST
seq [OPTIONS] FIRST LAST
seq [OPTIONS] FIRST INCR LAST
=head1 DESCRIPTION
seq writes a list of numbers to standard output separated by a newline character.
If only LAST is provided the sequence starts from 1 and the increment is 1.
LAST may be negative, in which case the sequence starts from 1 with the increment of -1.
When only FIRST and LAST are specified the increment will be either 1 or -1 based on whether FIRST is greater.
Sequences are inclusive of FIRST and LAST, so "seq 3 3" results in the sequence "3".
When an increment is needed other than 1 or -1, the INCR argument should be used.
Zero is not a valid increment.
Positive numbers may optionally include a '+' prefix.
Floating point numbers may be entered in decimal notation (e.g. 0.2223).
=head2 OPTIONS
The following options are available:
=over 4
=item -f FORMAT
Set a printf format specifier instead of the default '%g'
=item -s STRING
Separate each number with STRING instead of the newline character
=back
=head1 BUGS
Corrupt printf format specifiers may be entered.
=head1 AUTHOR
Written by Michael Mikonos.
=head1 COPYRIGHT
Copyright (c) 2023 Michael Mikonos.
This code is licensed under the Artistic License 2.
=cut