F# 对嵌套计算表达式的调用太多
F# too many calls to nested Computation Expression
这个问题是 的演变。我试图找出为什么当我在 CE 上 运行 State.exec
时,我似乎得到了不希望的 CE 嵌套行为。似乎在呼唤他们很多次。这是我拥有的:
type State<'a, 's> = ('s -> 'a * 's)
module State =
// Explicit
// let result x : State<'a, 's> = fun s -> x, s
// Less explicit but works better with other, existing functions:
let result x s =
x, s
let bind (f:'a -> State<'b, 's>) (m:State<'a, 's>) : State<'b, 's> =
// return a function that takes the state
fun s ->
// Get the value and next state from the m parameter
let a, s' = m s
// Get the next state computation by passing a to the f parameter
let m' = f a
// Apply the next state to the next computation
m' s'
/// Evaluates the computation, returning the result value.
let eval (m:State<'a, 's>) (s:'s) =
m s
|> fst
/// Executes the computation, returning the final state.
let exec (m:State<'a, 's>) (s:'s) =
m s
|> snd
/// Returns the state as the value.
let getState (s:'s) =
s, s
/// Ignores the state passed in favor of the provided state value.
let setState (s:'s) =
fun _ ->
(), s
type StateBuilder() =
member __.Return(value) : State<'a, 's> =
State.result value
member __.Bind(m:State<'a, 's>, f:'a -> State<'b, 's>) : State<'b, 's> =
State.bind f m
member __.ReturnFrom(m:State<'a, 's>) =
m
member __.Zero() =
State.result ()
member __.Delay(f) =
State.bind f (State.result ())
let rng = System.Random(123)
type StepId = StepId of int
type Food =
| Chicken
| Rice
type Step =
| GetFood of StepId * Food
| Eat of StepId * Food
| Sleep of StepId * duration:int
type PlanAcc = PlanAcc of lastStepId:StepId * steps:Step list
let state = StateBuilder()
let getFood =
state {
printfn "GetFood"
let randomFood =
if rng.NextDouble() > 0.5 then Food.Chicken
else Food.Rice
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = GetFood (nextStepId, randomFood)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return randomFood
}
type StateBuilder with
[<CustomOperation("sleep", MaintainsVariableSpaceUsingBind=true)>]
member this.Sleep (st:State<_,PlanAcc>, [<ProjectionParameter>] (duration: 'a -> int)) =
printfn $"Sleep"
let program d =
state {
let! x = st
printfn "Sleep: %A" duration
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Sleep (nextStepId, d)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}
State.bind (fun x -> program (duration x)) st
[<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
printfn $"Eat"
let program e =
state {
let! x = st
printfn "Eat: %A" food
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Eat (nextStepId, e)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}
State.bind (fun x -> program (food x)) st
let simplePlan =
state {
let! f1 = getFood
sleep 1
eat f1
sleep 2
eat f1
sleep 3
}
let initalAcc = PlanAcc(StepId 0, [])
let x = State.exec simplePlan initalAcc
这是我期望 x
:
> x;;
val it : PlanAcc =
PlanAcc
(StepId 6,
[Sleep (StepId 6, 3); GetFood (StepId 5, Chicken);
Sleep (StepId 4, Chicken); EatFood (StepId 3, Chicken);
Sleep (StepId 2, 1); GetFood (StepId 1, Chicken)])
这是我得到的:
> x;;
val it : PlanAcc =
PlanAcc
(StepId 63,
[Sleep (StepId 63, 3); Eat (StepId 62, Rice); Sleep (StepId 61, 2);
Eat (StepId 60, Rice); Sleep (StepId 59, 1);
GetFood (StepId 58, Chicken); GetFood (StepId 57, Chicken);
Sleep (StepId 56, 1); GetFood (StepId 55, Rice);
GetFood (StepId 54, Chicken); Eat (StepId 53, Chicken);
Sleep (StepId 52, 1); GetFood (StepId 51, Chicken);
GetFood (StepId 50, Chicken); Sleep (StepId 49, 1);
GetFood (StepId 48, Chicken); GetFood (StepId 47, Chicken);
Sleep (StepId 46, 2); Eat (StepId 45, Rice); Sleep (StepId 44, 1);
GetFood (StepId 43, Rice); GetFood (StepId 42, Chicken);
Sleep (StepId 41, 1); GetFood (StepId 40, Rice);
GetFood (StepId 39, Rice); Eat (StepId 38, Rice); Sleep (StepId 37, 1);
GetFood (StepId 36, Chicken); GetFood (StepId 35, Rice);
Sleep (StepId 34, 1); GetFood (StepId 33, Rice);
GetFood (StepId 32, Chicken); Eat (StepId 31, Rice);
Sleep (StepId 30, 2); Eat (StepId 29, Rice); Sleep (StepId 28, 1);
GetFood (StepId 27, Chicken); GetFood (StepId 26, Rice);
Sleep (StepId 25, 1); GetFood (StepId 24, Rice);
GetFood (StepId 23, Rice); Eat (StepId 22, Chicken);
Sleep (StepId 21, 1); GetFood (StepId 20, Rice);
GetFood (StepId 19, Chicken); Sleep (StepId 18, 1);
GetFood (StepId 17, Chicken); GetFood (StepId 16, Rice);
Sleep (StepId 15, 2); Eat (StepId 14, Rice); Sleep (StepId 13, 1);
GetFood (StepId 12, Rice); GetFood (StepId 11, Rice);
Sleep (StepId 10, 1); GetFood (StepId 9, Rice);
GetFood (StepId 8, Chicken); Eat (StepId 7, Chicken);
Sleep (StepId 6, 1); GetFood (StepId 5, Chicken);
GetFood (StepId 4, Chicken); Sleep (StepId 3, 1);
GetFood (StepId 2, Chicken); GetFood (StepId 1, Chicken)])
我相当确定它与 State
在 program
CE 中的绑定方式有关,因为如果我只调用 let! f = getFood
就没有问题很多次。
我尝试删除 program
CE 中的 let! x = st
和 return x
调用,认为这是导致问题的原因,但编译器在 simplePlan
CE 中抱怨说,“表达式应该有类型 'Food' 但这里有类型 'unit'”。
错误图片:
是的,你是对的:它确实与在 let! x = st
处绑定传入计算有关。
但是您也说对了,您不能只删除该绑定,因为您需要通过它的 return 值进行隧道传输,正如我在 .[=19= 中所描述的那样]
但这let! x = st
本身不是问题。
问题是您要绑定 st
两次:
[<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
printfn $"Eat"
let program e =
state {
let! x = st <b><-- here's the first time</b>
printfn "Eat: %A" food
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Eat (nextStepId, e)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}
State.bind (fun x -> program (food x)) st
<b>^^</b>
<b>here's the second time</b>
结果值加倍也就不足为奇了:您在每一步都进行了加倍的计算!
您需要删除其中 一个 ,但它不是 let! x = st
中的那个,因为您确实需要 x
这样您就可以 return x
最后。
[<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
printfn $"Eat"
state {
let! x = st
let f = food x
printfn "Eat: %A" f
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Eat (nextStepId, f)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}
这个问题是 State.exec
时,我似乎得到了不希望的 CE 嵌套行为。似乎在呼唤他们很多次。这是我拥有的:
type State<'a, 's> = ('s -> 'a * 's)
module State =
// Explicit
// let result x : State<'a, 's> = fun s -> x, s
// Less explicit but works better with other, existing functions:
let result x s =
x, s
let bind (f:'a -> State<'b, 's>) (m:State<'a, 's>) : State<'b, 's> =
// return a function that takes the state
fun s ->
// Get the value and next state from the m parameter
let a, s' = m s
// Get the next state computation by passing a to the f parameter
let m' = f a
// Apply the next state to the next computation
m' s'
/// Evaluates the computation, returning the result value.
let eval (m:State<'a, 's>) (s:'s) =
m s
|> fst
/// Executes the computation, returning the final state.
let exec (m:State<'a, 's>) (s:'s) =
m s
|> snd
/// Returns the state as the value.
let getState (s:'s) =
s, s
/// Ignores the state passed in favor of the provided state value.
let setState (s:'s) =
fun _ ->
(), s
type StateBuilder() =
member __.Return(value) : State<'a, 's> =
State.result value
member __.Bind(m:State<'a, 's>, f:'a -> State<'b, 's>) : State<'b, 's> =
State.bind f m
member __.ReturnFrom(m:State<'a, 's>) =
m
member __.Zero() =
State.result ()
member __.Delay(f) =
State.bind f (State.result ())
let rng = System.Random(123)
type StepId = StepId of int
type Food =
| Chicken
| Rice
type Step =
| GetFood of StepId * Food
| Eat of StepId * Food
| Sleep of StepId * duration:int
type PlanAcc = PlanAcc of lastStepId:StepId * steps:Step list
let state = StateBuilder()
let getFood =
state {
printfn "GetFood"
let randomFood =
if rng.NextDouble() > 0.5 then Food.Chicken
else Food.Rice
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = GetFood (nextStepId, randomFood)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return randomFood
}
type StateBuilder with
[<CustomOperation("sleep", MaintainsVariableSpaceUsingBind=true)>]
member this.Sleep (st:State<_,PlanAcc>, [<ProjectionParameter>] (duration: 'a -> int)) =
printfn $"Sleep"
let program d =
state {
let! x = st
printfn "Sleep: %A" duration
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Sleep (nextStepId, d)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}
State.bind (fun x -> program (duration x)) st
[<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
printfn $"Eat"
let program e =
state {
let! x = st
printfn "Eat: %A" food
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Eat (nextStepId, e)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}
State.bind (fun x -> program (food x)) st
let simplePlan =
state {
let! f1 = getFood
sleep 1
eat f1
sleep 2
eat f1
sleep 3
}
let initalAcc = PlanAcc(StepId 0, [])
let x = State.exec simplePlan initalAcc
这是我期望 x
:
> x;;
val it : PlanAcc =
PlanAcc
(StepId 6,
[Sleep (StepId 6, 3); GetFood (StepId 5, Chicken);
Sleep (StepId 4, Chicken); EatFood (StepId 3, Chicken);
Sleep (StepId 2, 1); GetFood (StepId 1, Chicken)])
这是我得到的:
> x;;
val it : PlanAcc =
PlanAcc
(StepId 63,
[Sleep (StepId 63, 3); Eat (StepId 62, Rice); Sleep (StepId 61, 2);
Eat (StepId 60, Rice); Sleep (StepId 59, 1);
GetFood (StepId 58, Chicken); GetFood (StepId 57, Chicken);
Sleep (StepId 56, 1); GetFood (StepId 55, Rice);
GetFood (StepId 54, Chicken); Eat (StepId 53, Chicken);
Sleep (StepId 52, 1); GetFood (StepId 51, Chicken);
GetFood (StepId 50, Chicken); Sleep (StepId 49, 1);
GetFood (StepId 48, Chicken); GetFood (StepId 47, Chicken);
Sleep (StepId 46, 2); Eat (StepId 45, Rice); Sleep (StepId 44, 1);
GetFood (StepId 43, Rice); GetFood (StepId 42, Chicken);
Sleep (StepId 41, 1); GetFood (StepId 40, Rice);
GetFood (StepId 39, Rice); Eat (StepId 38, Rice); Sleep (StepId 37, 1);
GetFood (StepId 36, Chicken); GetFood (StepId 35, Rice);
Sleep (StepId 34, 1); GetFood (StepId 33, Rice);
GetFood (StepId 32, Chicken); Eat (StepId 31, Rice);
Sleep (StepId 30, 2); Eat (StepId 29, Rice); Sleep (StepId 28, 1);
GetFood (StepId 27, Chicken); GetFood (StepId 26, Rice);
Sleep (StepId 25, 1); GetFood (StepId 24, Rice);
GetFood (StepId 23, Rice); Eat (StepId 22, Chicken);
Sleep (StepId 21, 1); GetFood (StepId 20, Rice);
GetFood (StepId 19, Chicken); Sleep (StepId 18, 1);
GetFood (StepId 17, Chicken); GetFood (StepId 16, Rice);
Sleep (StepId 15, 2); Eat (StepId 14, Rice); Sleep (StepId 13, 1);
GetFood (StepId 12, Rice); GetFood (StepId 11, Rice);
Sleep (StepId 10, 1); GetFood (StepId 9, Rice);
GetFood (StepId 8, Chicken); Eat (StepId 7, Chicken);
Sleep (StepId 6, 1); GetFood (StepId 5, Chicken);
GetFood (StepId 4, Chicken); Sleep (StepId 3, 1);
GetFood (StepId 2, Chicken); GetFood (StepId 1, Chicken)])
我相当确定它与 State
在 program
CE 中的绑定方式有关,因为如果我只调用 let! f = getFood
就没有问题很多次。
我尝试删除 program
CE 中的 let! x = st
和 return x
调用,认为这是导致问题的原因,但编译器在 simplePlan
CE 中抱怨说,“表达式应该有类型 'Food' 但这里有类型 'unit'”。
错误图片:
是的,你是对的:它确实与在 let! x = st
处绑定传入计算有关。
但是您也说对了,您不能只删除该绑定,因为您需要通过它的 return 值进行隧道传输,正如我在 但这 问题是您要绑定 结果值加倍也就不足为奇了:您在每一步都进行了加倍的计算! 您需要删除其中 一个 ,但它不是 let! x = st
本身不是问题。st
两次:
[<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
printfn $"Eat"
let program e =
state {
let! x = st <b><-- here's the first time</b>
printfn "Eat: %A" food
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Eat (nextStepId, e)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}
State.bind (fun x -> program (food x)) st
<b>^^</b>
<b>here's the second time</b>
let! x = st
中的那个,因为您确实需要 x
这样您就可以 return x
最后。 [<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
printfn $"Eat"
state {
let! x = st
let f = food x
printfn "Eat: %A" f
let! (PlanAcc (StepId lastStepId, steps)) = State.getState
let nextStepId = StepId (lastStepId + 1)
let newStep = Eat (nextStepId, f)
let newAcc = PlanAcc (nextStepId, newStep::steps)
do! State.setState newAcc
return x
}