在F#中尝试finally语句

时间:2016-01-15 13:48:03

标签: f#

我是新手学习F#并编写一个简单的控制台应用程序,用户输入一个距离值,我想要一些验证以确保输入是一个数字。它还需要确保它是一个数字,如果不是,请告诉用户并重新开始。这就是我到目前为止所做的:

        let distance = 0

        while distance = 0 do 
            System.Console.WriteLine("How far do you want to travel?")
            let answer = System.Console.ReadLine()

            try
                let distance = System.Int32.Parse(answer)

                if distance < 0 then
                    let distance = 0
                    printfn "Can't use negative numbers"
                elif distance = 0 then
                    printfn "Can't travel a distance of 0"
                else
                    printfn "You are about to travel %A" distance

            finally
                let distance = 0
                printfn "Invalid distance format"

这就是出现的问题:

![Results of console app

在这个例子中,我想要发生的是“无效距离格式”到不是,并且它会移动到应用的下一部分。

如果distance无法通过System.Int32.Parse(answer)

转换为int,我将如何制作“无效距离格式”

即使try-finally是完全错误的方法,还有什么办法呢?

提前致谢

3 个答案:

答案 0 :(得分:7)

你想要的是try...with

   try
      let distance = System.Int32.Parse(answer)
      . . . . .

   with
   | _ as ex -> printfn "Invalid Distance Format"

http://fsharpforfunandprofit.com/posts/exceptions/

您也可以执行类似

的操作
...
let attemptedConvert =  Int32.TryParse(answer)
let success,convertValue = attemptedConvert
if success then 
  //other stuff here
else 
  printfn "Invalid Number Format"

这样就不会抛出异常,如果条目成功转换,您仍然可以进行验证。

如下所述,异常可能是比其他替代方案更昂贵的操作,应进行评估以查看是否会导致不必要的开销。但是,与任何流程一样,根据具体情况进行评估。

TryParse Method

答案 1 :(得分:3)

在你学习的过程中,我会通过做这样的事情来解决这个问题。

它使用了对于处理'null'情况(即没有输入)有用的选项类型。它还使用F#模式匹配,这是使用if,else,elif等的非常强大的替代方法。

//see here - http://fsharpforfunandprofit.com/posts/the-option-type/
let tryParseOption intStr = 
   try
      let i = System.Int32.Parse intStr
      Some i
   with _ -> None

type Ask = 
static member Askdistance (?text)= 
        let text = defaultArg text "" 
        printfn "%s" text

        System.Console.WriteLine("How far do you want to travel?")
        let distance = tryParseOption (System.Console.ReadLine())

        match distance with 
            |None                  -> Ask.Askdistance("Invalid format distance provided")
            |Some(a) when a <0     -> Ask.Askdistance("Can't use negative numbers")
            |Some(a) when a =0     -> Ask.Askdistance("Can't travel a distance of 0")
            |_                     -> printfn "You are about to travel %A" distance.Value
        ignore()

//this will keep asking you to input a distance until you put in a correct value (i.e. a positive integer value. Note it will reject a floating point input). 
Ask.Askdistance()

答案 2 :(得分:1)

虽然其他答案是正确的,但这是另一种方法:

open System

let rec travel() =
    printfn "How far do you want to travel?"
    let d = Console.ReadLine()
    match Int32.TryParse d with
    | false, _ ->
        printfn "Invalid distance format '%s'" d
        travel()
    | _, 0 ->
        printfn "Can't travel a distance of 0"
        travel()
    | _, d when d < 0 ->
        printfn "Can't use negative number %i" d
        travel()
    | _, d ->
        printfn "You are about to travel %i" d

travel()

在我看来,这有以下优点:

  • 不使用例外
  • 对同一事物不使用不同的概念(printfn vs Console.WriteLine
  • 不会引入不必要的构造(类型,现有功能的辅助函数)
  • 不会混淆责任(下一次调用的打印错误)

我同意,学习一门新语言应该探索解决方案空间。但是选择一种简洁,优雅的方式。

我的答案的其他一些方面可能是固执己见。你可能想要

  • 使用显式true代替_
  • 为匹配的已解析距离选择其他名称。我使用阴影,因为我认为解析的值只是一个不同的表示。
  • 重新排序案例,例如拥有'快乐路径'首先类似于try...catch

    open System
    
    let rec travel() =
        printfn "How far do you want to travel?"
        let d = Console.ReadLine()
        match Int32.TryParse d with
        | true, d when d > 0 ->
            printfn "You are about to travel %i" d
        | true, 0 ->
            printfn "Can't travel a distance of 0"
            travel()
        | true, _ ->
            printfn "Can't use negative number %i" d
            travel()
        | _ ->
            printfn "Invalid distance format '%s'" d
            travel()
    
    travel()
    

另一种方法是从控制台分离阅读和旅行

  1. 使用始终返回int的阅读功能:

    open System
    
    let rec readInt() =
        let d = Console.ReadLine()
        match Int32.TryParse d with
        | true, d ->
            d
        | _ ->
            printfn "Invalid distance format '%s'" d
            readInt()
    
    let rec travel() =
        printfn "How far do you want to travel?"
        let d = readInt()
        if d > 0 then
            printfn "You are about to travel %i" d
        elif d = 0 then
            printfn "Can't travel a distance of 0"
            travel()
        else
            printfn "Can't use negative number %i" d
            travel()
    
    travel()
    
  2. 使用可能失败的阅读功能:

    open System
    
    let rec readInt() =
        let d = Console.ReadLine()
        match Int32.TryParse d with
        | true, d -> Some d
        | _ -> None
    
    let rec travel() =
        printfn "How far do you want to travel?"
        match readInt() with
        | Some d when d > 0 ->
            printfn "You are about to travel %i" d
        | Some d when d = 0 ->
            printfn "Can't travel a distance of 0"
            travel()
        | Some d ->
            printfn "Can't use negative number %i" d
            travel()
        | None ->
            printfn "Invalid distance format '%s'" d
            travel()
    
    travel()