F#对嵌套计算表达式的调用过多 [英] F# too many calls to nested Computation Expression

查看:58
本文介绍了F#对嵌套计算表达式的调用过多的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题是

解决方案

是的,您是对的:它确实与在 let上绑定传入的计算有关!x = st .

但是您也很正确,您不能仅删除该绑定,因为您需要通过它的返回值进行隧道传输,如我在<-这是第一次printfn吃:%A"食物让!(PlanAcc(StepId lastStepId,步骤))= State.getState让nextStepId = StepId(lastStepId + 1)让newStep = Eat(nextStepId,e)让newAcc = PlanAcc(nextStepId,newStep :: steps)做!State.setState newAcc返回x}State.bind(趣味X->程序(食物X))ST ^^ 这是第二次

绝对怪不得结果值翻倍:您在每一步都将计算翻倍!

您需要删除其中的一个 ,但这不是 let中的一个!x = st ,因为您确实需要 x 以便您可以最后返回x .

  [< CustomOperation(吃",MaintainsVariableSpaceUsingBind = true)>]成员this.Eat(st:State< _,PlanAcc> ;, [< ProjectionParameter>](food:'a-> Food))=printfn $吃"状态 {让!x = st令f =食物xprintfn"Eat:%A"F让!(PlanAcc(StepId lastStepId,步骤))= State.getState让nextStepId = StepId(lastStepId + 1)让newStep = Eat(nextStepId,f)让newAcc = PlanAcc(nextStepId,newStep :: steps)做!State.setState newAcc返回x} 

This questions is an evolution of this question. I am trying to find out why when I run State.exec on the CE that I appear to be getting undesired nesting behavior of the CEs. It seems to be calling them many times. Here is what I have:

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

Here is what I expect to get for 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)])

Here is what I get:

> 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)])

I'm fairly certain it has to do with how the State is being bound in the program CEs since I don't have a problem if I just call let! f = getFood a bunch of times.

I tried removing the let! x = st and return x calls in the program CEs thinking that was what was causing the issue, but the compiler complained in the simplePlan CE saying, "The expression was expected to have type 'Food' but here has type 'unit'".

Image of error:

解决方案

Yes, you're right: it does have to do with binding the incoming computation at let! x = st.

But you're also right that you can't just remove that binding, because you need to tunnel through its return value, as I described in the previous answer.

But this let! x = st is not a problem by itself.

The problem is that you're binding st twice:


    [<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
    member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
        printfn $"Eat"
        let program e =
            state {
                let! x = st  <-- here's the first time
                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
                                               ^^
                                               here's the second time

It's absolutely no wonder the resulting values double: you're doing double the computation at every step!

You need to remove one of them, but it's not the one in let! x = st, because you do need that x so that you can return x at the end.

    [<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
        }
 

这篇关于F#对嵌套计算表达式的调用过多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆