This is my response to The Weekly Challenge #333.
Input: @list = ([2, 1], [2, 3], [2, 5])
Output: true
Example 2:
Input: @list = ([1, 4], [3, 4], [10, 4])
Output: true
Example 3:
Input: @list = ([0, 0], [1, 1], [2, 3])
Output: false
Example 4:
Input: @list = ([1, 1], [1, 1], [1, 1])
Output: true
Example 5:
Input: @list = ([1000000, 1000000], [2000000, 2000000],
[3000000, 3000000])
Output: true
The five examples have three points, but the challenge text does not say that this is the case. So I have chosen to support an arbitrary number of points - with at least three.
File: straight-line
#! /usr/bin/env raku
unit sub MAIN (*@list where @list.elems > 2
&& all(@list) ~~ /^\d+\,\d+$/, # [1]
:v(:$verbose));
my ($Ax, $Ay) = @list.shift.split(",")>>.Int; # [2]
my ($Bx, $By) = @list.shift.split(",")>>.Int; # [3]
while @list # [4]
{
my ($Cx, $Cy) = @list.shift.split(",")>>.Int; # [5]
say ": Checking if ($Ax,$Ay), ($Bx,$By), ($Cx,$Cy) forms a triangle"
if $verbose;
if $Ax * ($By - $Cy) + $Bx * ($Cy - $Ay) + $Cx * ($Ay - $By) # [6]
{
say ": trangle detected" if $verbose;
say False; # [6a]
exit; # [6b]
}
}
say True; # [7]
[1] Ensure at least three points (or strings, rather).
[2] Get the first string, and extract the point coordinates Ax
and Ay
by splitting it on a comma. Then we coerce the two values
to integers so that verbose mode looks better.
[3] Get the second string.
[4] As long as we have more points (no 3, 4 and so on) to process.
[5] Get the next string, and extract the third point for our comparison. We will get a new third point for each additional value in the input.
[6] Do we have a triangle, i.e. it has a non-zero area. If so, they are not on a straight line and we have faled. Say so and exit.
[7] Failure to fail means success. Say so.
I got the area formula from www.sciencing.com, but have dropped the final division by 2 as we are only checking for zero.
Running it:
$ ./straight-line "2,1" "2,3" "2,5"
True
$ ./straight-line "1,4" "3,4" "10,4"
True
$ ./straight-line "0,0" "1,1" "2,3"
False
$ ./straight-line "1,1" "1,1" "1,1"
True
$./straight-line "1000000,1000000" "2000000,2000000" "3000000,3000000"
True
Looking good.
With verbose mode:
$ ./straight-line -v "2,1" "2,3" "2,5"
: Checking if (2,1), (2,3), (2,5) forms a triangle
True
$ ./straight-line -v "1,4" "3,4" "10,4"
: Checking if (1,4), (3,4), (10,4) forms a triangle
True
$ ./straight-line -v "0,0" "1,1" "2,3"
: Checking if (0,0), (1,1), (2,3) forms a triangle
: trangle detected
False
$ ./straight-line -v "1,1" "1,1" "1,1"
: Checking if (1,1), (1,1), (1,1) forms a triangle
True
$./straight-line -v "1000000,1000000" "2000000,2000000" "3000000,3000000"
: Checking if (1000000,1000000), (2000000,2000000), (3000000,3000000) \
forms a triangle
True
Some more, with more than three points:
$ ./straight-line -v "0,0" "1,1" "2,2" "3,3" "4,4" "5,5"
: Checking if (0,0), (1,1), (2,2) forms a triangle
: Checking if (0,0), (1,1), (3,3) forms a triangle
: Checking if (0,0), (1,1), (4,4) forms a triangle
: Checking if (0,0), (1,1), (5,5) forms a triangle
True
$ ./straight-line -v "0,0" "1,1" "2,2" "3,3" "4,4" "5,5" "60,6"
: Checking if (0,0), (1,1), (2,2) forms a triangle
: Checking if (0,0), (1,1), (3,3) forms a triangle
: Checking if (0,0), (1,1), (4,4) forms a triangle
: Checking if (0,0), (1,1), (5,5) forms a triangle
: Checking if (0,0), (1,1), (60,6) forms a triangle
: trangle detected
False
$ ./straight-line -v "0,0" "1,1" "2,2" "60,6" "3,3" "4,4" "5,5"
: Checking if (0,0), (1,1), (2,2) forms a triangle
: Checking if (0,0), (1,1), (60,6) forms a triangle
: trangle detected
False
Input: @ints = (1, 0, 2, 3, 0, 4, 5, 0)
Output: (1, 0, 0, 2, 3, 0, 0, 4)
Each zero is duplicated.
Elements beyond the original length (like 5 and last 0) are discarded.
Example 2:
Input: @ints = (1, 2, 3)
Output: (1, 2, 3)
No zeros exist, so the array remains unchanged.
Example 3:
Input: @ints = (1, 2, 3, 0)
Output: (1, 2, 3, 0)
Example 4:
Input: @ints = (0, 0, 1, 2)
Output: (0, 0, 0, 0)
Example 5:
Input: @ints = (1, 2, 0, 3, 4)
Output: (1, 2, 0, 0, 3)
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 0 && all(@ints) ~~ Int); # [1]
say @ints.map({ $_ ?? $_ !! (0,0) })[*;*][0 .. @ints.end];
# ## # 2 ######################### # 3 # # 4 ############
[1] Ensure at least one element, all of which must be integers.
[2] Use map
to replace zeroes with (0,0)
(a sublist with
two zeroes), whilst non-zero values are left as is.
[3] The [*;*]
construct flattens out the lists we added in [2],
giving two zeroes instead of a (one) sublist containing two zeroes.
[4] This array slice takes the same number of elements
as in the original array, from the start. The end
call gives
us the index of last item in the list. (You will get the same result
with elems -1
end
is shorter.)
See docs.raku.org/routine/end for more information about end
.
Running it:
$ ./duplicate-zeroes 1 0 2 3 0 4 5 0
(1 0 0 2 3 0 0 4)
$ ./duplicate-zeroes 1 2 3
(1 2 3)
$ ./duplicate-zeroes 1 2 3 0
(1 2 3 0)
3$ ./duplicate-zeroes 0 0 1 2
(0 0 0 0)
$ ./duplicate-zeroes 1 2 0 3 4
(1 2 0 0 3)
Looking good.
Verbose mode did not fit in the almost-one-liner format of the solution.
And that's it.