折叠功能未产生预期结果

Fold function does not produce expected result

以下折叠函数将重复的球员加载到基地记录中:

    (initializedBase, plays) ||> List.fold (fun bases play -> 
                                                Some play.Player |> move bases)

测试如下:

[<Test>]
let ``2 players each hit single``() =
    // Setup
    let players = [{ Player= Scott; Hit= Single }; { Player= Brian; Hit= Single }]

    // Test
    let bases = players |> assignBases

    bases |> should equal { First=Some Brian; Second=Some Scott; Third=None }

实际结果是:

{ First=Some Scott; Second=Some Scott; Third=None }

我预计:

{ First=Some Brian; Second=Some Scott; Third=None }

完整代码在这里:

module Foo

(*Types*)
type Position =
    | First
    | Second
    | Third 

type Player =
    | Scott
    | Brian
    | Cherice

type Hit =
    | Single
    | Double
    | Triple

type Play = { Player: Player; Hit: Hit }

type Bases = { 
    First:Player  option
    Second:Player option
    Third:Player  option
}

(*Functions*)
let assignBases (plays:Play list) =

    let initializedBase = { First=None; Second=None; Third=None }

    let move bases player =

        match bases with
        | { First= None;   Second=None;   Third=None }   -> { bases with First=player }
        | { First= player; Second=None;   Third=None }   -> { bases with First=player; Second=bases.First }
        | { First= None;   Second=player; Third=None }   -> { bases with First=None; Second=None; Third=player }
        | { First= None;   Second=None;   Third=player } -> { bases with First=player }
        | _ -> initializedBase // fill the rest out later...

    (initializedBase, plays) ||> List.fold (fun bases play -> 
                                                Some play.Player |> move bases)

(*Tests*)
open NUnit.Framework
open FsUnit

[<Test>]
let ``2 players each hit single``() =
    // Setup
    let players = [{ Player= Scott; Hit= Single }; { Player= Brian; Hit= Single }]

    // Test
    let bases = players |> assignBases

    bases |> should equal { First=Some Brian; Second=Some Scott; Third=None }

看起来问题与 Seq.fold 本身没有任何关系。如果我没记错的话,错误在move函数中匹配表达式的第二行:

| { First=player; Second=None; Third=None } -> { bases with First=player; Second=bases.First }

该模式使用 player 捕获 basesFirst 字段的值。尽管看起来应该如此,但这确实 not 将其与匹配表达式外部的 player 的值匹配!如果您需要,则需要另外使用 when 子句。

实际上,从 F# 确定第二种情况是正确的那一刻起(SecondThirdNoneFirst 具有Some 值),直到控制离开 整个 匹配表达式,名称 player 表示一个新的绑定,据说是 "shadowing" 旧的 player值。

因此,当您在该匹配案例的结果部分将 player 分配给 First 时,它将再次成为 bases.First 的原始值 - 与您随后设置的值相同Second.

所以你希望第二个匹配案例是

| { First=oldFirst; Second=None; Third=None } -> { bases with First=player; Second=oldFirst }

现在,player 仍然引用与匹配表达式外相同的值,即您要在 First 处输入的字段,而 Second 获取玩家你 "picked up" 来自 First.