I have a while-do loop nested inside a while-do loop in my program, due to it needing to iterate in two dimensions through an array of arrays, and originally my loop looked like this:
while ( tHeightIndex < tSearchHeight - 1 ) && tContinue do
while ( tWidthIndex < tSearchWidth - 1 ) && tContinue do
let tCurrentValue = tLargeArray.[tHeightIndex].[tWidthIndex]
if tCurrentValue = tFirstSmallPixel then
tMatch <- ArrayFunctions.SearchSubset tSmallArray tLargeArray ( tHeightIndex, tWidthIndex )
if tMatch then tContinue <- false
if tMatch = false && tContinue = true then
tWidthIndex <- tWidthIndex + 1
if tMatch = false && tContinue = true then
tWidthIndex <- 0 // Reset to search next row
tHeightIndex <- tHeightIndex + 1
tMatch, tWidthIndex, tHeightIndex
However, seeing as I'm doing this project entirely to learn better functional programming practices, I refactored that nested loop into two tail-recursive functions. This code passes my unit tests and appears to work correctly, so I would like advice about the style of the code and whether there's better ways I can make the code read clearly.
The relevant part to the original while-do loop is located after the declaration of firstSmallPixel
.
module ImageSearch =
open ImageFunctions
let SearchBitmap (smallBitmap:Bitmap) (largeBitmap:Bitmap) =
let smallArray = Transform2D <| LoadBitmapIntoArray smallBitmap
let largeArray = Transform2D <| LoadBitmapIntoArray largeBitmap
let searchWidth = largeBitmap.Width - smallBitmap.Width
let searchHeight = largeBitmap.Height - smallBitmap.Height
let firstSmallPixel = smallArray.[0].[0]
let WidthLoop heightIndex =
let rec WidthLoopRec heightIndex widthIndex =
let ContinueLoop () = WidthLoopRec heightIndex (widthIndex + 1)
let currentLargePixel = largeArray.[heightIndex].[widthIndex]
match ( widthIndex < searchWidth , currentLargePixel = firstSmallPixel ) with
| ( true , true ) -> let foundImage = ArrayFunctions.SearchSubset smallArray largeArray ( heightIndex, widthIndex )
if foundImage then widthIndex , foundImage
else ContinueLoop ()
| ( true , false ) -> ContinueLoop ()
| ( false, _ ) -> widthIndex , false
WidthLoopRec heightIndex 0
let HeightLoop () =
let rec HeightLoopRec heightIndex =
let widthIndex, foundImage = WidthLoop heightIndex
match ( foundImage, heightIndex < searchHeight ) with
| ( false , true ) -> HeightLoopRec ( heightIndex + 1 )
| ( _ , _ ) -> foundImage , widthIndex , heightIndex
HeightLoopRec 0
HeightLoop ()
1 Answer 1
I think that learning functional programming should be about making your code more readable (and also better in other aspects), neither of your samples seems very readable to me.
I also think that when working with collections in functional programming, the most important concept is not recursion, it's higher-order functions.
If you use those in the form of a query expression, your code could look like this:
query {
for row in 0..searchHeight do
for col in 0..searchWidth do
let pixel = largeArray.[row].[col]
where (pixel = firstSmallPixel)
select (row, col, pixel)
head
}
The nice thing about this code is not only that it's more readable and shorter, it's that it emphasizes what you want to do, not how to do it.
Explore related questions
See similar questions with these tags.