patternMinor
Testing whether a matrix contains a submatrix
Viewed 0 times
matrixwhethertestingcontainssubmatrix
Problem
As part 2 of my other question concerning this long running project I've been inquiring about on CR, I reimplemented my generic function for determining if an array of arrays is a sub-array or submatrix of a larger array of arrays.
I'm looking for advice on better implementation, ways to improve code clarity, advice on best practices and anything else that might be useful. This is my new implementation:
I ran this code through my tests, an
I'm looking for advice on better implementation, ways to improve code clarity, advice on best practices and anything else that might be useful. This is my new implementation:
namespace MathAlgorithms
module ArrayFunctions =
// Generic because it makes testing easier, yay math
let IsSubMatrix (smallArray:'a[][]) (largeArray:'a[][]) (startCoordinate:(int * int)) =
let searchHeight , searchWidth = smallArray.Length - 1 , smallArray.[0].Length - 1
let startHeight , startWidth = startCoordinate
try
let WidthLoop heightIndex =
let rec WidthLoopRec heightIndex widthIndex =
let largeValue = largeArray.[startHeight + heightIndex].[startWidth + widthIndex]
let smallValue = smallArray.[heightIndex].[widthIndex]
match ( smallValue = largeValue , widthIndex WidthLoopRec heightIndex (widthIndex + 1)
| ( true , false ) -> true
| ( false , _ ) -> false
WidthLoopRec heightIndex 0
let HeightLoop () =
let rec HeightLoopRec heightIndex =
let isMatch = WidthLoop heightIndex
match ( isMatch , heightIndex HeightLoopRec ( heightIndex + 1 )
| ( true , false ) -> true
| ( false , _ ) -> false
HeightLoopRec 0
HeightLoop ()
with // Not really sure what I want to do with error handling atm
| :? System.ArgumentOutOfRangeException -> false
| :? System.ArgumentNullException -> false
| :? System.ArgumentException -> falseI ran this code through my tests, an
Solution
A few suggestions regarding the style:
-
There's no need for parentheses in the pattern matching blocks. Those are evaluated as tuples by the very fact that they are comma separated:
and:
-
In
-
The function
-
-
I think it would be better to handle the exceptions before getting into the actual computation. In fact, if the code base is done in F#, there should be no
This should also account for out-of-range indexing. If a mathematical operation makes no sense, the typing itself should not allow it to be made.
You could use FsCheck to test your code with random values to see that your code never raises exceptions. Logically, "no answer" should not be represented as
-
Your function name should probably reflect more clearly their intention. I'm not a mathematician, but I guess you would mean something like
-
There's no need for parentheses in the pattern matching blocks. Those are evaluated as tuples by the very fact that they are comma separated:
match smallValue = largeValue, widthIndex WidthLoopRec heightIndex (widthIndex + 1)
| true, false -> true
| false, _ -> falseand:
match isMatch , heightIndex HeightLoopRec (heightIndex + 1)
| true, false -> true
| false, _ -> false-
In
HeightLoopRec, there's no need for isMatch. You can use the result of calling WidthLoop directly inside the pattern matching (maybe with parentheses to make it more legible):match (WidthLoop heightIndex), heightIndex < searchHeight with-
The function
HeightLoop is not needed and can be removed and replaced by its contents, making HeightLoopRec the top level function. This is obvious by its signature (unit -> bool). It doesn't take anything as input, unlike its internal tail-recursive function whose signature is int -> bool.-
WidthLoop can be inlined (let inline WidthLoop heightIndex =).-
I think it would be better to handle the exceptions before getting into the actual computation. In fact, if the code base is done in F#, there should be no
null values (as those would be replaced by option types). Within your code domain, illegal values should be made unrepresentable, as the famous motto goes. This should also account for out-of-range indexing. If a mathematical operation makes no sense, the typing itself should not allow it to be made.
You could use FsCheck to test your code with random values to see that your code never raises exceptions. Logically, "no answer" should not be represented as
false. If a function may return no value at all or a bool, then its return type should be bool option, where None would represent the lack of a valid result.-
Your function name should probably reflect more clearly their intention. I'm not a mathematician, but I guess you would mean something like
isRowContained (instead of WidthLoop), or whatever makes sense.Code Snippets
match smallValue = largeValue, widthIndex < searchWidth with
| true, true -> WidthLoopRec heightIndex (widthIndex + 1)
| true, false -> true
| false, _ -> falsematch isMatch , heightIndex < searchHeight with
| true, true -> HeightLoopRec (heightIndex + 1)
| true, false -> true
| false, _ -> falsematch (WidthLoop heightIndex), heightIndex < searchHeight withContext
StackExchange Code Review Q#59314, answer score: 2
Revisions (0)
No revisions yet.