3
\$\begingroup\$

I'm learning F#. The code below gets a number e.g. 100 and list of factors and should divide that number by the factors without any rest. (There is an actual code I've got in C#, but this is F# port).

I especially didn't liked equalsDifference function.. any better solution?

namespace RozbijaczApp
module Rozbijacz =
    let precision = 2

    let minUnit = 1m / (pown 10m precision)

    let round x = System.Math.Round(decimal x, precision, System.MidpointRounding.AwayFromZero)

    let mainMapper factor factorSum x = 
        round (x * factor / factorSum)

    let equalsDifference minUnit (difference: decimal) (l: decimal list) =
        let listSize = List.length l
        let mutable acc = difference

        [for i in [0..listSize-1] do
            let element = l |> List.item i
            if acc <> 0m then
                if difference < 0m then
                    if element >= minUnit then
                        acc <- acc + minUnit
                        yield element - minUnit
                    else
                        yield element
                else
                    acc <- acc - minUnit
                    yield element + minUnit
            else    
                yield element
        ]

    let divider (number:decimal) factors = 
        let factorsSum = factors |> List.sum 
        let result = factors |> List.map (fun x -> mainMapper number factorsSum x)
        let controlSum = result |> List.sum 
        let difference = number - controlSum
        printfn "Difference %O" difference
        // add or substract diffenernce by minUnit for results
        let fixResult = equalsDifference minUnit difference result
        let finalControlSum = fixResult |> List.sum 
        let finalDifference = number - finalControlSum
        printfn "Final difference %O should by zero"finalDifference

        fixResult

and usage:

        let factors = [0.2m;0.3m;0.4m]
        let results = Rozbijacz.divider 100m factors
        printfn "Result: %A" results

ok, as you suggested map is better:

let equalsDifference minUnit (difference: decimal) (l: decimal list) =
    let listSize = List.length l
    let mutable acc = difference

    let resp = l |> List.map (fun element -> (
        if acc <> 0m then
            if difference < 0m then
                if element >= minUnit then
                    acc <- acc + minUnit
                    element - minUnit
                else
                    element
            else
                acc <- acc - minUnit
                element + minUnit
        else    
            element
        ) )
    resp

But is this enough? Any F# magic?

\$\endgroup\$
  • \$\begingroup\$ Regarding equalsDifference, why don't you use List.map over l? \$\endgroup\$ – Quentin Pradet Jun 30 '16 at 12:19
  • \$\begingroup\$ The for loop should be List.map \$\endgroup\$ – John Palmer Jun 30 '16 at 12:19
  • 1
    \$\begingroup\$ @JohnPalmer Please write all critiques, even trivial ones, in an answer. Comments are for seeking to clarify the question, and may be deleted. \$\endgroup\$ – 200_success Jun 30 '16 at 16:27
2
\$\begingroup\$

Instead of using mutable variables, it is possible to apply the fold:

let equalsDifference minUnit difference l =
    l
    |> List.fold
        (fun (acc,xs) element -> 
               if acc <> 0m then
                  if difference < 0m then
                     if element >= minUnit then
                         acc + minUnit, (element - minUnit)::xs
                     else
                         acc, element::xs
                  else
                     acc - minUnit, (element + minUnit)::xs
               else
                  acc, element::xs)
        (difference, [])
    |> snd
    |> List.rev
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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