F# 从 while 循环中中断

问题描述 投票:0回答:7

有什么办法可以做到像

C/C#
那样吗?

例如(C#风格)

for (int i = 0; i < 100; i++)
{
   if (i == 66)
       break;
} 
f# while-loop break
7个回答
42
投票

简短的回答是否定的。您通常会使用一些高阶函数来表达相同的功能。有许多函数可以让您执行此操作,对应于不同的模式(因此,如果您描述了您到底需要什么,有人可能会给您更好的答案)。

例如,

tryFind
函数返回给定谓词返回
true
的序列中的第一个值,这使您可以编写如下内容:

seq { 0 .. 100 } |> Seq.tryFind (fun i ->
  printfn "%d" i
  i=66)

在实践中,如果你要表达一些高级逻辑并且有相应的函数,这是最好的方法。如果你确实需要表达像

break
这样的东西,你可以使用递归函数:

let rec loop n = 
  if n < 66 then 
    printfn "%d" n
    loop (n + 1)

loop 0      

一个更奇特的选项(效率不高,但可能对 DSL 来说很好)是您可以定义一个计算表达式,让您编写

break
continue
这是一个例子,但正如我所说,这效率不高。


14
投票
let mutable Break = false
while not Break do
    //doStuff

    if breakCondition then
        Break <- true
done

这对于 do-while 循环很有用,因为它保证循环至少执行一次。


4
投票

你必须将其更改为 while 循环。

let (i, ans) = (ref 0, ref -1)
while(!i < 100 and !ans < 0) do
 if !i = 66 then
   ans := !i
ans

(当我达到 66 时,这会中断 - 但是是的,语法完全不同,引入了另一个变量,等等)


3
投票
seq { 
    for i = 0 to 99 do
        if i = 66 then yield ()
}
|> Seq.tryItem 0
|> ignore

2
投票

试试这个:

exception BreakException

try
    for i = 0 to 99 do
      if i = 66 then
        raise BreakException
with BreakException -> ()

我知道有些人不喜欢使用异常。但它有其优点。

  • 你不必考虑复杂的递归函数。的 因为你可以这样做,但有时会带来不必要的麻烦 并且使用异常更简单。

  • 此方法允许您在循环体的一半处中断。 (中断“标志”方法也很简单,但它只允许在循环体末尾中断。)

  • 您可以轻松摆脱嵌套循环。


0
投票

对于此类问题,您可以使用递归函数。

let rec IfEqualsNumber start finish num =
    if start = finish then false
    elif 
      start = num then true
    else
      let start2 = start + 1
      IfEqualsNumber start2 finish num

0
投票

最近我尝试解决类似的情况:

例如 10 条数据的列表。它们中的每一个都必须针对 Restful 服务器进行查询,然后获得每个结果。

let lst = [4;6;1;8]

问题:

  • 如果 API 调用失败(例如网络问题),则没有必要进行进一步的调用,因为我们需要所有 10 个结果可用。当 API 调用失败时,整个过程应尽快停止。

天真的方法:使用
List.map()

lst |> List.map (fun x -> 
    try
        use sqlComd = ...
        sqlComd.Parameters.Add("@Id", SqlDbType.BigInt).Value <- x
        sqlComd.ExecuteScala() |> Some
    with
        | :? System.Data.SqlClient.SqlException as ex -> None
)

但正如我所说,这并不是最佳选择。当发生失败的 API 时,剩余的项目将继续被处理。无论如何,他们做了一些最终被忽略的事情。

黑客方法:使用
List.tryFindIndex()

map()
不同,我们必须将结果存储在 lamda 函数中的某个位置。合理的选择是使用
mutable
列表。因此,当
tryFindIndex()
返回
None
时,我们知道一切正常,可以开始使用
mutable
列表。

val myList: List<string>
let res = lst |> List.tryFindIndex (fun x ->
    try
        use sqlComd = ...
        sqlComd.Parameters.Add("@Id", SqlDbType.BigInt).Value <- x
        myList.Add(sqlComd.ExecuteScala())
        false
    with
        |:? System.Data.SqlClient.SqlException as ex -> true
)

match res with
| Some _ -> printfn "Something went wrong"
| None -> printfn "Here is the 10 results..."

惯用的方法:使用递归

不太惯用,因为它使用异常来停止操作。

exception MyException of string
let makeCall lstLocal =
    match lstLocal with
    | [] -> []
    | head::tail ->
        try
            use sqlComd = ...
            sqlComd.Parameters.Add("@Id", SqlDbType.BigInt).Value <- x
            let temp = sqlComd.ExecuteScala()
            temp :: makeCall (tail)
        with
            |:? System.Data.SqlClient.SqlException as ex -> raise MyException ex.Message

try
    let res = makeCall lst
    printfn "Here is the 10 results..."
with
    | :? MyException -> printfn "Something went wrong"

老式命令式方法:
while... do

这还是涉及到

mutable
列表。

© www.soinside.com 2019 - 2024. All rights reserved.