折叠功能未产生预期结果
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
捕获 bases
的 First
字段的值。尽管看起来应该如此,但这确实 not 将其与匹配表达式外部的 player
的值匹配!如果您需要,则需要另外使用 when
子句。
实际上,从 F# 确定第二种情况是正确的那一刻起(Second
和 Third
是 None
和 First
具有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
.
以下折叠函数将重复的球员加载到基地记录中:
(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
捕获 bases
的 First
字段的值。尽管看起来应该如此,但这确实 not 将其与匹配表达式外部的 player
的值匹配!如果您需要,则需要另外使用 when
子句。
实际上,从 F# 确定第二种情况是正确的那一刻起(Second
和 Third
是 None
和 First
具有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
.