开发者

Something similar to yield break in F#

开发者 https://www.devze.com 2022-12-15 12:13 出处:网络
How to break after first if? 开发者_如何学Golet WithdrawalCash (account, amount) = seq { if ( account.Balance.CurrentAmount - amount < 0.0m) then

How to break after first if?

开发者_如何学Golet WithdrawalCash (account, amount) = seq {        

    if ( account.Balance.CurrentAmount - amount < 0.0m) then
        yield NotEnoughMoneyForWithdrawal(account, amount)   
        // How to break here?     

    let newBalance = account.Balance.CurrentAmount - amount
    yield CashWithdrawnEvent(account, newBalance)
}


Not sure this will help, why not use the else clause?

let WithdrawalCash (account, amount) = seq {        

   if ( account.Balance.CurrentAmount - amount < 0.0m) then
       yield NotEnoughMoneyForWithdrawal(account, amount)   
       // How to break here?     
   else 
      let newBalance = account.Balance.CurrentAmount - amount
      yield CashWithdrawnEvent(account, newBalance) 
}

Also have a look at:

Imperative computation in F# (II.) - Writing break and continue


The code as posted will only evern return one CashWithdrawlEvent, then end the sequence... you need to have a loop to return multiple values. Also, have you considered using "match" to handle multiple cases?

(not tested as working...)

let WithdrawalCash (account, amount) = seq {         

    let bDone = ref false

    while not (!bDone) do
        match amount with
        | v when account.Balance.CurrentAmount - v < 0 ->
           yield NotEnoughMoneyForWithdrawal(account, amount)
           bDone := true    // break

        // more when clauses can go here

        | _ ->
          let newBalance = account.Balance.CurrentAmount - amount 
          yield CashWithdrawnEvent(account, newBalance) 
          // let the sequence continue

}

But, even this does not seem like what you would want, since it will ALWAYS withdraw the same 'amount' each time you pull a value from the sequence, because account and amount are fixed when the sequence is created. So, I'd drop the 'seq' and make this a simple function, as in:

let WithdrawalCash (account, amount) =

    match amount with
    | v when account.Balance.CurrentAmount - v < 0 ->
       NotEnoughMoneyForWithdrawal(account, amount)

    // more when clauses can go here

    | _ ->
      let newBalance = account.Balance.CurrentAmount - amount 
      CashWithdrawnEvent(account, newBalance) 

For the general case of enumerating some sequence and stopping when a particular condition is met, consider "Seq.takeWhile", as in:

let MySeq = seq {
    while true do
        yield someValue
    }

let code () =
    MySeq
    |> Seq.takeWhile ( fun v -> ShouldIContinueWorkingTest(v) )
    |> Seq.iter ( fun v -> DoWork(v) )


As this is the only relevant question in SO about yield break in F#, I think I should add this for the sake of completeness. In the OP's question one could get away with the else. But what do you do if you really need to break? what if you have to deal with match? Let's assume that you have a discriminated union like this

type SomeUnion =
    | Foo of SomeType
    | Bar of SomeType * SubclassOfSomeType
    | Indifferent of IncompatibleType

and you want to get a sequence of SomeType and SubclassOfSomeType and ignore the rest:

let private toSomeType (arg:SomeUnion) =
    seq {
        match arg with
        | Foo foo -> yield foo
        | Bar (one, two) -> yield one; yield two
        | Indifferent _ -> () (*<- effectively this is your 'yield break'*)
    }

I used Indifferent as it was my only remaining case. Obviously one could simply use | _ -> () to ignore a gazillion of cases, I just prefer to see the warning if I add another case in the future.

I hope that this is a correct technique as I am an absolute beginner in F# and I had to chase my tail for some time to get around this. Feel free to let me know in the comments if there is a better way.

0

精彩评论

暂无评论...
验证码 换一张
取 消