带有集和更新的F#语法

时间:2016-11-08 12:04:51

标签: algorithm list f# set

在F#中我试图在满足条件的情况下删除集合中的出现,但是它并不是真正按照我希望的方式工作。

2 个答案:

答案 0 :(得分:4)

从集合中删除元素的技巧是函数Set.filter,它将函数作为参数 - filter将函数的每个值提供给函数,并将其添加到函数中如果函数返回true,则为新集。示例实现可能是:

let filter f (original : Set<'T>) =
    set [ for value in original do if f value then yield value ]

,其类型为filter : ('T -> bool) -> Set<'T> -> Set<'T>。使用它的一个例子是

filter (fun x -> x % 2 = 0) (set [ 1; 2; 3; 4; 5 ])

这会过滤偶数的集合,因此返回值为set [ 2; 4 ]

我不完全确定你究竟遇到了什么问题,但这里有一个使用Knuth算法的游戏Mastermind的解决方案,虽然是随机启动猜测,而不是他选择的“1122”。

我认为这是一个非常好的练习,尽管写checkGuess函数对我来说是最难的部分!

您可以通过运行函数playMastermind ()在F#interactive中打开它来运行测试,这将显示其猜测。

/// The colours that pegs are allowed to be.
type Peg = Blue | Red | Green | Yellow | Purple | Brown

/// A shared instance of the System.Random () class for all the random number
/// generators.
let private rnd = new System.Random ()

/// Make a random set of four peg colours.
let randomGuess () =
    let randomPeg () =
        match rnd.Next(1, 6) with
        | 1 -> Blue
        | 2 -> Red
        | 3 -> Green
        | 4 -> Yellow
        | 5 -> Purple
        | 6 -> Brown
        | _ -> failwith "Random number generation failed."
    [ randomPeg (); randomPeg (); randomPeg (); randomPeg () ]

/// Iterate over the colours to make all of the possible combinations.
let allPossibles =
    let colours = [ Blue; Red; Green; Yellow; Purple; Brown]
    set [ for a in colours do for b in colours do for c in colours do for d in colours -> [ a; b; c; d ] ] 

/// Get the number of white and black pegs when comparing solution to guess.
let checkGuess solution guess =
    /// Create a map of (colour -> count).
    let toMap = List.countBy id >> Map.ofList
    /// Compute how many pegs' colours are shared in the guesses.
    let mapIntersect map1 map2 =
        let overlap peg count =
            match Map.tryFind peg map2 with
            | None -> 0
            | Some num -> min num count
        Map.fold (fun acc peg count -> acc + overlap peg count) 0 map1
    /// Simply compare to see if each peg is in the correct place.
    let blacks = List.map2 (fun x y -> if x = y then 1 else 0) solution guess |> List.sum
    // The number of pegs of the right colour but the wrong location is the
    // same as the total number of pegs of the right colour subtract the ones
    // that are also in the right place.
    let whites = mapIntersect (toMap solution) (toMap guess) - blacks
    whites, blacks

/// Get a random element of a set.
let randomSetElement set =
    let arr = Set.toArray set
    arr.[rnd.Next (Array.length arr)]

let playMastermind () =
    // This creates a closure so we can check our guess against the solution,
    // without storing the actual value of the solution.
    let checkAnswer = checkGuess (randomGuess ())
    let rec loop turnCount remaining =
        if Set.count remaining = 1 then
            let answer = Set.maxElement remaining
            printfn "The answer is %A, which I calculated in %d turns." answer (turnCount - 1)
        else
            let guess = randomSetElement remaining
            let (whites, blacks) = checkAnswer guess
            printfn "On turn %d I guessed %A, which gave %d white pins and %d black pins." turnCount guess whites blacks
            /// Remove all possibilities from the solution that wouldn't give the
            /// same numbers of white and black pins and continue.
            loop (turnCount + 1) (Set.filter (fun possible -> (whites, blacks) = checkGuess possible guess) remaining)
    // Play the game!
    loop 1 allPossibles

答案 1 :(得分:1)

我将此作为评论,但它太长了,所以它需要成为答案,即使它不是完整的答案你的问题。

现在,您的代码存在一个问题:

for candidate in candidateSet do
    let scString = candidate.ToString()

    let mutable secretList = []
    for i = 0 to 3 do
        let digit = (int scString.[i])-(int '0')
        secretList <- secretList @ [digit]
    let tempCode = List.map (fun x -> numberToCodeColorPlus (x)) secretList
//Validate works and returns a peg set (b,w)..e.g. (0,0)    
let secretCodePegs = validate guess tempCode
    if secretCodePegs <> guessPegs then
        candidateSet <- Set.remove candidate candidateSet

与Python一样,F#使用缩进来表示块。因此let secretCodePegs = validate guess tempCode <{1}}循环,而不是for循环内部,就像您明确指出的那样。就F#而言,其后面的for行是 new 块的一部分,而不再是if secretCodePegs <> guessPegs then循环的一部分(因为{ {1}}行结束了for循环)。您需要做的就是将let secretCodePegs = ...行缩进一级,您的代码才能正常工作。换句话说,部分应该是这样的:

for