Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
750 views
in Technique[技术] by (71.8m points)

f# - 功能至上(Imperative to Functional)

I have been doing a CodeWars exercise which can also be seen at dev.to .

(我一直在进行CodeWars练习,也可以在dev.to上看到。)

The essence of it is:

(它的本质是:)

There is a line for the self-checkout machines at the supermarket. Your challenge is to write a function that calculates the total amount of time required for the rest of the customers to check out!

INPUT
customers : an array of positive integers representing the line. Each integer represents a customer, and its value is the amount of time they require to check out.
n : a positive integer, the number of checkout tills.

RULES
There is only one line serving many machines, and
The order of the line never changes, and
The front person in the line (i.e. the first element in the array/list) proceeds to a machine as soon as it becomes free.

OUTPUT
The function should return an integer, the total time required.

The answer I came up with works - but it is highly imperative.

(我想出了答案,但这是非常必要的。)

open System.Collections.Generic
open System.Linq

let getQueueTime (customerArray: int list) n =
    let mutable d = new Dictionary<string,int>()
    for i in 1..n do
        d.Add(sprintf "Line%d" <| i, 0) 

    let getNextAvailableSupermarketLineName(d:Dictionary<string,int>) =
        let mutable lowestValue = -1
        let mutable lineName = ""
        for myLineName in d.Keys do
            let myValue = d.Item(myLineName)
            if lowestValue = -1 || myValue <= lowestValue then
              lowestValue <- myValue
              lineName <- myLineName
        lineName

    for x in customerArray do
        let lineName = getNextAvailableSupermarketLineName d
        let lineTotal = d.Item(lineName)
        d.Item(lineName) <- lineTotal + x

    d.Values.Max()

So my question is ... is this OK F# code or should it be written in a functional way?

(所以我的问题是……这是可以的F#代码还是应该以功能性方式编写?)

And if the latter, how?

(如果是后者,怎么办?)

(I started off trying to do it functionally but didn't get anywhere).

((我开始尝试从功能上做到这一点,但没有成功)。)

  ask by Alan James translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Here is a version that resembles your version, with all the mutability removed:

(这是一个与您的版本相似的版本,其中删除了所有可变性:)

let getQueueTime (customerArray: int list) n =
    let updateWith f key map =
        let v = Map.find key map
        map |> Map.add key (f v)

    let initialLines = [1..n] |> List.map (fun i -> sprintf "Line%d" i, 0) |> Map.ofList

    let getNextAvailableSupermarketLineName(d:Map<string,int>) =
        let lowestLine = d |> Seq.minBy (fun l -> l.Value)
        lowestLine.Key

    let lines =
        customerArray
        |> List.fold (fun linesState x ->
            let lineName = getNextAvailableSupermarketLineName linesState
            linesState |> updateWith (fun l -> l + x) lineName) initialLines

    lines |> Seq.map (fun l -> l.Value) |> Seq.max

getQueueTime [5;3;4] 1 |> printfn "%i"

Those loops with mutable "outer state" can be swapped for either recursive functions or folds / reduce , here I suspect recursive functions would be nicer.

(那些具有可变“外部状态”的循环可以交换为递归函数或folds / reduce ,这里我怀疑递归函数会更好。)

I've swapped out Dictionary for the immutable Map , but it feels like more trouble than it's worth here.

(我已将Dictionary换成不可变的Map ,但感觉麻烦多于这里的价值。)

Update - here is a compromise solution I think reads well:

(更新 -这是我认为很好的折衷解决方案:)

let getQueueTime (customerArray: int list) n =
    let d = [1..n] |> List.map (fun i -> sprintf "Line%d" i, 0) |> dict

    let getNextAvailableSupermarketLineName(d:IDictionary<string,int>) =
        let lowestLine = d |> Seq.minBy (fun l -> l.Value)
        lowestLine.Key

    customerArray
    |> List.iter (fun x ->
        let lineName = getNextAvailableSupermarketLineName d
        d.Item(lineName) <- d.Item(lineName) + 1)

    d.Values |> Seq.max

getQueueTime [5;3;4] 1 |> printfn "%i"

I believe there is a more natural functional solution if you approach it freshly, but I wanted to evolve your current solution.

(我相信,如果您刚开始使用,将有一个更自然的功能解决方案,但是我想发展您当前的解决方案。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

56.7k users

...