3
\$\begingroup\$

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 ()
asked Aug 6, 2014 at 20:21
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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.

answered Aug 7, 2014 at 19:24
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.