Learning F#: Case study with branch and bound – Part I (page 4)

First, a few variables and functions were renamed to be more reflective of what they do, e.g. the function unsetVar was renamed to unsetVarIndex as it is returning an index (or -1) to an unset variable not the variable. tvar was the actual unset variable, that will be renamed to unsetVar. But the other improvement is to the code is in the area that creates new arrays with zero/one settings substituted for the Unset variable. This is that code as it stood:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
let tvar = Array.get curVars varIndex

// change the unset var to One and Zero
let zeroVar = { tvar with Setting = Zero }
let varArrayZero = Array.copy curVars  // copy the array so we can change element
Array.set varArrayZero varIndex zeroVar
let gEstimateZero = estimator wlimit varArrayZero

let oneVar = { tvar with Setting = One }
let varArrayOne = Array.copy curVars  // copy the array so we can change element
Array.set varArrayOne varIndex oneVar
let gEstimateOne = estimator wlimit varArrayOne    

Along with these variable renames, consider another approach to the beginning part of the while loop:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
while not queue.IsEmpty do
    match queue with
    | curVars :: restOfQueue -> 
        let unsetVarIndex = unsetVarIndex curVars 
        if not (unsetVarIndex = -1) then

            let varArrayZero = Array.mapi (fun index var -> if index = unsetVarIndex then {var with Setting = Zero} else var) curVars
            let varArrayOne  = Array.mapi (fun index var -> if index = unsetVarIndex then {var with Setting = One} else var) curVars  

The first version created a copy of the curVar array, a new element and then used Array.set to change the array. This of course works, but the code above is another choice for implementation that avoids intermediate values. It uses the Array.mapi function which creates a new array, applying a function with an integer (index) and element argument to each array member. In our case, when the index is the same as the unset var’s index we return the variable with Zero or One as a setting, otherwise leave it as is. This is applied to a copy of the array which is the last argument to the Array.mapi function. As an alternative, the last argument (curVars) can be piped to the function (see piping). This can sometimes make the code easier to read, for example:

1: 
2: 
let varArrayZero = curVars |> Array.mapi (fun index var -> if index = unsetVarIndex then {var with Setting = Zero} else var) 
let varArrayOne  = curVars |> Array.mapi (fun index var -> if index = unsetVarIndex then {var with Setting = One} else var)  

The pipeline operator is left associative so h |> g |> f groups as (h |> g) |> f

The pipeline operator (|>)doesn’t introduce any new capabilities, it is just, as they say, ‘syntactic sugar’ for styling the code. That is for a value b and function a (that accepts b as an argument), each of these means the same thing: a b and b |> a. The idea is that the result b is piped to a as a’s last argument. So an expression such as f (g h) could be written as h |> g |> f. Anyway, this final expression using mapi for defining each of varArrayZero and varArrayOne is more readable and understandable as to what is trying to be accomplished, versus the ‘array set’ method before. Use the download button to see the complete code at this point.