You are given an integer.
Write a script find out the middle 3-digits of the given integer, if possible otherwise throw sensible error.
Input: $n = 1234567
Output: 345
Input: $n = -123
Output: 123
Input: $n = 1
Output: too short
Input: $n = 10
Output: even number of digits
We need to do a couple of things, all fairly trivial:
The third step technically isn't required, as it's a given we're getting an integer. But we check anyway.
Note that a two digit integer fails validation twice: it's too short, and it has an even number of digits. The fourth example shows we should give an error for the second failure (and not the first).
As usual, we will be accepting multiple integers, assuming one integer per line of input. For each integer, we'll print the middle three digits, or an error if necessary.
AWK is very suitable to repeatedly do something to a line of input, and to repeat this for each line of input.
We start off with stripping a leading sign, if any:
/^[-+]/ {$0 = substr ($0, 2)}
$0
holds the current line of input, and it's an lvalue, so we can
assign to it.
We can now do the validation. If it fails a validation, we print an error message, and continue with the next line of input:
/[^0-9]/ {print "not an integer"; next}
/^(..)*$/ {print "even number of digits"; next}
length ($0) < 3 {print "too short"; next}
We can now print the middle three digits:
{print substr ($0, 1 + (length ($0) - 3) / 2, 3)}
substr
in AWK takes three arguments: the string we want to
extract a substring from; the position from where to start (first
character is on position 1), and the length of the substring.
Find the full program on GitHub.
We first start off reading a line of input using read
:
while read line; do
First, we strip off a leading sign, if any, using parameter expansion:
line=${line/#[-+]/}
This takes what is in the variable line
, and performs a substitution:
replacing #[-+]
with nothing (because there is nothing following
the second slash. Where most languages use ^
to indicate the
beginning of a string, here, this role is played by #
.
We now check whether we are left with just digits:
if [[ $line =~ [^0-9] ]]
then echo "not an integer"
[[
introduces Bash Conditional
Expressions. The expression results in a true/false result. Here,
we are matching line
against a regular expression. Note that we
don't need a delimiter here.
Checking for the right amount of digits:
elif ((${#line} % 2 == 0))
then echo "even number of digits"
elif ((${#line} < 3))
then echo "too short"
((
introduces Shell Arithmetic.
If we have passed all validations, we can print the right substring:
echo ${line:$(((${#line} - 3) / 2)):3}
We do this with several other parameter expansions.
${#line}
is the first parameter expansion. The result is the length
of the string in line
.
$(((${#line} - 3) / 2))
takes what is inside $(( ))
and treats
it as an arithmetic expression. The result is the start index of
substring we want.
We use another type of parameter expansion to get the substring:
${line:position:length}
gets length
characters from line
,
starting at position position
.
Find the full program on GitHub.
We start off with reading a line from standard input. getline
will do the job:
char * line = NULL;
size_t len = 0;
size_t str_len;
while ((str_len = getline (&line, &len, stdin)) != -1) {
The string (with terminating newline) will be put into the character
array line
, while the return value of getline
is the length
of the line. (getline
will do the malloc
ing for us, and
populate len
with the number of bytes allocated; we will not
use len
in our program).
We start with removing a leading sign, if any (if there is any space following the sign, we will remove this as well):
char * line_ptr = line;
if (* line_ptr == '-' || * line_ptr == '+') {
str_len --;
line_ptr ++;
while (isspace (* line_ptr)) {
line_ptr ++;
str_len --;
}
}
The result is that afterwards, line_ptr
will point to the first
character following the sign.
Next step is to make sure all the other characters are digits. Note that our string still contains a newline:
bool done = false;
char * line_ptr2 = line_ptr;
while (* line_ptr2 && * line_ptr2 != '\n') {
if (!isdigit (* line_ptr2)) {
printf ("not an integer\n");
done = true;
break;
}
line_ptr2 ++;
}
if (done) {
continue;
}
C cannot break out of nested loops, so we determine the input
contains a non-digit, we set a boolean, and do the
continue
after we're done with the inner loop.
We can now check we have the right amount of digits (while remembering we have a trailing newline):
if (str_len % 2 == 1) {
printf ("even number of digits\n");
continue;
}
if (str_len < 4) {
printf ("too short\n");
continue;
}
We can print the middle three digits, which we print one by one, followed by a newline:
size_t mid = (str_len - 3) / 2;
for (size_t i = 0; i < 3; i ++) {
printf ("%c", line_ptr [mid + i]);
}
printf ("\n");
It's C, so, we shouldn't forget the free
allocated
memory:
free (line);
Find the full program on GitHub.
First, we have to read a line from standard input:
var reader = bufio . NewReader (os. Stdin)
main_loop:
for {
var text, err = reader . ReadString ('\n')
if (err != nil) {
break
}
Stripping off the newline we do with TrimRight
in the string
module:
text = strings . TrimRight (text, "\n")
Go has immutable strings, so we have a leading sign, we take a slice from the string, and assign that back to the string:
if (text [0:1] == "-" || text [0:1] == "+") {
text = text [1:]
}
We now check whether the string contains just digits. For that, we iterate over the runes of the string:
for _, rune := range text {
if rune < '0' || rune > '9' {
fmt . Print ("not an integer\n")
continue main_loop
}
}
We can now check for the correct number of digits.
len
returns the length of a string:
if len (text) % 2 == 0 {
fmt . Print ("even number of digits\n")
continue main_loop
}
if len (text) < 3 {
fmt . Print ("too short\n")
continue main_loop
}
Now we can print the required digits, using a slice:
fmt . Printf ("%s\n", text [(len (text) - 3) / 2 :
(len (text) + 3) / 2])
Find the full program on GitHub.
Java requires even more work to be done to just read a line of input:
Scanner scanner = new Scanner (System . in);
try {
while (true) {
String line = scanner . nextLine ();
This time, we first check whether we have an integer, by using a regular
expression. The
matches
function in the Pattern
class
tries to match the given pattern against the given string:
if (!Pattern . matches ("^[-+]?[0-9]+$", line)) {
System . out . println ("not an integer");
continue;
}
If the string starts with a sign, we get rid of it. Java strings are immutable, so we take a substring and assign it back to the string:
if (Pattern . matches ("^[-+].*", line)) {
line = line . substring (1);
}
We can now check for the right amount of digits:
int ll = line . length ();
if (ll % 2 == 0) {
System . out . println ("even number of digits");
continue;
}
if (ll < 3) {
System . out . println ("too short");
continue;
}
Finally, we print the wanted digits:
System . out . println (line . substring ((ll - 3) / 2,
(ll + 3) / 2));
Find the full program on GitHub.
First, we strip off the sign. line
contains
a line from standard input:
line = line : gsub ("^[-+]%s*", "")
Our set of validations:
if line : find ("%D") then
print ("not an integer")
else
if line : len () % 2 == 0 then
print ("even number of digits")
else
if line : len () < 3 then
print ("too short")
And the printing of the middle digits:
local start = 1 + (line : len () - 3) / 2
print (line : sub (start, start + 2))
Find the full program on GitHub.
First, we strip off the sign and the trailing newline. line
contains
a line from standard input:
line . replace (/^[-+]/, '') . trim ()
replace
takes two arguments, a pattern, and a replacement,
while trim
removes whitespace from either end of the string.
Neither method changes a string — they return the modified string.
We now do the validations as a sequence of if/else
statements:
if (line . match (/[^0-9]/)) {
console . log ("not an integer")
}
else {
if (line . length % 2 == 0) {
console . log ("even number of digits")
}
else {
if (line . length < 3) {
console . log ("too short")
}
}
}
match
takes a pattern as argument, and returns true if the
pattern matches the string it's applied to.
Finally, the printing:
console . log (line . substr ((line . length - 3) / 2, 3))
Find the full program on GitHub.
Perl has powerful regexes: it has the ability to run code, and dynamically create sub-patterns.
We use this to validate the number, and select the middle digits at the same time; if it fails, we do additional checks, all using regular expressions to find out why it fails:
say /^[-+]?([0-9]*)([0-9]{3})([0-9]*)$
(??{length ($1) == length ($3) ? "" : "(*FAIL)"})/x
? $2
: /^[-+]?[0-9]*[^0-9].*\n/ ? "not an integer"
: /^[-+]?(?:[0-9][0-9])*\n/ ? "even number of digits"
: "too short"
$_
contains the line of input (with a trailing newline). The first
regular expression does the validation, and selecting of the middle
digits. We can break down the expression as follows:
/^ # Match from the beginning
[-+]? # An optional sign
([0-9]*) # Zero or more digits; capture group $1
([0-9]{3}) # Exactly three digits; capture group $2
([0-9]*) # Zero or more digits; capture group $3
$ # End of string
(??{ # Run the following code, and treat its value
# as a sub-pattern to be matched at this point.
length ($1) == length ($3) # If capture groups $1 and $3 are same length:
? "" # Match an empty string (always succeeds)
: "(*FAIL)" # Else fail (and backtrack)
})/x
The latter part, forcing that the first and third capture group
have the same number of digits, means that if the entire match
succeeds, $2
contains the middle three digits: it must be the
middle three, as the entire number (without sign) is $1$2$3
,
and $1
and $3
contain the same amount of digits.
If the match fails, we need to find out why it fails:
/^[-+]?[0-9]*[^0-9].*\n/
This pattern matches if the input line contains, except a leading sign and trailing newline, a character which is not a digit.
/^[-+]?(?:[0-9][0-9])*\n/
This pattern matches if, and only if, the input line contains an even number of digits (note that if we execute this pattern, we already know all the characters between the beginning (and possible sign) and the trailing newline are all digits.
If this last pattern fails as well, the only possibility which is left, is that the input number contains less than three digits.
Find the full program on GitHub.
The Python solution follows the same strategy as most implementations.
First, we remove any leading sign, and the trailing newline. line
contains our line of input:
line = re . sub (r'^[-+]', '', line . strip ())
String are immutable in Python, so all the methods return the modified string, without modifying the original.
Validating and printing the middle three digits is easy:
ll = len (line)
if re . search (r'[^0-9]', line):
print ("not an integer")
elif ll % 2 == 0:
print ("even number of digits")
elif ll < 3:
print ("too short")
else:
print (line [(ll - 3) // 2 : (ll + 3) // 2])
Find the full program on GitHub.
The Ruby solution looks very similar to the Python solution. The
current line of input is in the variable line
:
line = line . strip() . gsub(/^[-+]/, "")
ll = line . length
if line . match (/[^0-9]/) then
puts ("not an integer")
elsif ll % 2 == 0 then
puts ("even number of digits")
elsif ll < 3 then
puts ("too short")
else
puts (line [((ll - 3) / 2) . to_i .. ((ll + 2) / 2) . to_i])
end
Find the full program on GitHub.
Our Scheme solution isn't very different from other solutions — it just looks different:
(define line (read-line))
This creates a variable line
, and we initialize it with a line from
standard input.
We first check whether the input contains an integer, and we capture the numbers:
(set! is-number (string-match "^[-+]?([0-9]+)$" line))
string-match
takes two arguments, and matches the first argument
(the pattern) against the second argument. If the match succeeds,
a match object is returned; else, #f
is returned.
We first check whether the match succeed; if it doesn't, we print the appropriate message:
(if (not is-number)
(display "not an integer")
...
If it does match, not is-number
is fails, so the if
statement executes
its third argument (represented by ...
above). This third argument is:
(begin
(set! number (match:substring is-number 1))
(set! ll (string-length number))
(if (= (modulo ll 2) 0)
(display "even number of digits")
(if (< ll 3)
(display "too short")
(display (substring number (/ (- ll 3) 2)
(/ (+ ll 3) 2)))
)
)
)
Remember the capture in the first pattern? The match object can be
queried for that using match:substring
. This function takes the
arguments, a match object, and the number of the capture we want.
In our case, this is the number without sign.
string-length
returns the length of its argument.
The rest of the code is fairly straightforward. Remember that in scheme, all operators are postfix operators (and as such, there isn't any difference between operators and functions).
Find the full program on GitHub.
Our Tcl solution starts with:
if {[regexp {^[-+]?([0-9]+)$} $line -> digits]} {
...
} else {
puts "not an integer"
}
where $line
contains the current line of input. regexp
takes
at least two parameters: a pattern, a value to match against.
Subsequent parameters, if any, are use to write captures to,
where the first capture is the entire match; each subsequent capture
is a next group of parens. Here ->
isn't a funny piece of syntax,
it is the third argument to regexp
, indicating we don't need
the entire match. But we do want what was matched by the first set
of parens: this is written to the variable digits
.
If the match fails, we're printing not an integer
. Else, we run what's
in the placeholder ...
:
set ll [string length $digits]
if {[expr $ll % 2] == 0} {
puts "even number of digits"
} elseif {[expr $ll < 3]} {
puts "too short"
} else {
puts [string range $digits [expr ($ll - 3) / 2]\
[expr ($ll + 2) / 2]]
Find the full program on GitHub.