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 ()
\$\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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.