2009-08-17 46 views
1

我一直在學習F#,並且正在寫一個工具。一切正常,除非我需要處理無效輸入或異常情況。如何處理異常輸入並停止運行F#中的其餘功能?

在F#中處理這個問題的建議方法是什麼?我的函數被構造和使用的方式我不知道如何發回一條消息,告訴調用函數由於錯誤的輸入以及類似於C#的循環繼續需要發生調用失敗。

由於回報的損失,我很痛苦。我可以從F#函數的任何位置返回,還是必須在最後一行?

這是我的一些代碼的例子,其中輸入在某些情況下基本上是空的,我正在檢測它。我需要阻止運行過程中的步驟。

let SchemaQueryString s = 
    let sqsValid s = 
     let Strcat (x : string) (y : string) = x + ", " + y; 
     let RecFieldName (x : MeasurementRecord) = x.FieldName;  
     let mapr = List.map RecFieldName s.MeasurementTypes; 
     let cores = List.reduce Strcat mapr; 
     "SELECT ProductionCode, TestTime, " + cores + " FROM " + s.TemplateName + " ORDER BY ProductionCode, TestTime"; 

    match s.MeasurementTypes.Length with 
    | 0 -> "" 
    | _ -> sqsValid s; 

let TemplateMigration tcs rr = 
    let sql = SchemaQueryString tcs; 
    let sqlc = new SqlCommand(sql, QCOld); 
    let sqldr = sqlc.ExecuteReader(); 
    ignore (sqldr.Read()); 
    ... 

我需要中止TemplateMigration功能,並以「」上一場比賽的SQL變量的立即返回。或者構造某種類型的聯合類型,表示不能構造sql查詢字符串,因爲對於特定情況它不能完成。

感謝您的幫助。我逐漸想出了這種語言,我對它很喜歡,但是我正在努力解決它的某些方面,我不知道如何取代我的關於如何用功能性思想做某些事情的程序性思想。

回答

8

當第一次使用函數式語言時,很多人翹首以盼一切都是表達式。這意味着控制流程使用控制流程表達式執行。

那麼聲明和表達之間有什麼區別?

一個語句沒有值,即它不是一個類型的實例。一種表達具有一個值(即使這個值是具有單個實例()它表示沒有值的特殊類型unit的。)

在我們使用語句來控制執行流程所以程序語言:

class Program 
{ 
    static void Main(string[] args) 
    { 
     string str; 

     if (DateTime.Now.DayOfWeek == DayOfWeek.Monday) 
      return; 
     else if (args.Length >= 2) 
      str = args[1]; 
     else 
      str = "Hello"; 

     Console.WriteLine(str); 
    } 
} 

這裏我們定義string類型的變量str爲具有值null。然後,我們要麼忽略它,要麼是return,如果是星期一,則指定args[1](如果可用)或"Hello",否則打印。

當控制流量表只有幾行時,這種編程相當安全。然而,只要語句長於一頁,或複雜並嵌套,我們就更可能引入錯誤,通常是billion dollar mistake

純功能語言改爲使用控制流表達式。這意味着if表達式有一個值!讓我們慢慢實現前面代碼示例中的控件。首先讓我們解決指定字符串str

let str = 
    if args.Length >= 2 then 
     args.[1] 
    else 
     "Hello" 

在這裏,我們看到,我們的if表達式的值直接綁定到strif表達式的兩個分支(子表達式)必須評估爲string類型的實例。我們不能忘記爲字符串賦值,因爲如果我們嘗試,編譯器會拋出一個錯誤。

但是,似乎有很多情況下,我們希望在控制流的不同分支中執行不同類型的事情。在我們的例子中,如果是星期一,我們不想做任何事情。這是歧視工會的力量發揮作用的地方。這些允許我們將異構類型組合成一個整體,保留基於表達式的語言的能力,以便在每個級別對我們的代碼進行類型檢查。

在我們的案例中,我們不需要創建新的聯合類型,因爲option類型已包含此功能。因此,我們改寫這樣:

open System 

[<EntryPoint>] 
let Main args = 

    let str = 
     if DateTime.Now.DayOfWeek = DayOfWeek.Monday then 
      None 
     elif args.Length >= 2 then 
      Some(args.[1]) 
     else 
      Some("Hello") 

    match str with 
    | Some(str) -> Console.WriteLine(str) 
    | None  ->() 

    Environment.ExitCode 

現在我們從if表達式返回的值是string option(或Option<string>在C#),我們再使用模式匹配安全地與我們的控制流的所有可能的結果處理。

當您返回從TemplateMigrationunit值,你可以簡單的返回從if表達這樣的兩個分支相同的值:

let TemplateMigration tcs rr = 
    let sql = SchemaQueryString tcs 
    if sql = null || sql.Length = 0 then 
     () 
    else 
     let sqlc = new SqlCommand(sql, QCOld) 
     let sqldr = sqlc.ExecuteReader() 
     ignore (sqldr.Read()) 

但是它可能會更好地從SchemaQueryString

返回 option
let SchemaQueryString s = 
    ... // Elided 

    match s.MeasurementTypes.Length with 
    | 0 -> None 
    | _ -> Some(sqsValid s) 

在整個代碼中'傳播'強類型值。

0

與其他語言一樣,您可以隨時返回。插入一個條件,返回一個特殊的值(-1,「錯誤」,等等)爲壞的數據,繼續處理,否則返回良好的數據。或者使用F#的exception handling methods

+0

所以,如果我有一個函數返回單位類型,我可以只是做 如果條件then()和函數將退出,如果條件滿足? – 2009-08-17 14:32:44

+0

想想你的代碼將如何評估。函數式編程增加了一些有趣的怪癖,但大部分行都是按順序執行的。控制語句(如IF和RETURN)會中斷正常流程。將剩餘的代碼放入ELSE中,然後代碼塊將無所事事,但返回NULL,這是一個特殊值,其他函數可以將其解釋爲無效數據。 – mcandre 2009-08-17 14:43:00

+1

不,你不能只是做「如果條件then()」,因爲那樣會完全評估一個單位,如果它不是最後一個表達式,就會被忽略。你需要一個其他的東西,這個東西到達其他的功能。例如** let test x = if String.IsNullOrEmpty x then(); printfn「有價值」; printfn「最後一點工作」;; **如果用其他替換,則會看到最終打印兩行,而不管輸入如何。 – MichaelGG 2009-08-18 01:21:50

4

其他人已經發布了兩個最好的方法來提前返回:if-then-else(或等價的模式匹配)和異常。還有第三種方法是編寫monad異常,並使用F#計算表達式語法來獲得「隱式返回失敗」代碼路徑。這是一種更先進的技術,除非你在許多函數中編寫了很多if-then-else子句,否則我會推薦它,在這一點上計算表達式可能開始是「勝利」。

在這個特定的例子中,對於ScemaQueryString返回一個「字符串選項」是最習慣的,例如,有回是

match s.MeasurementTypes.Length with  
| 0 -> None 
| _ -> Some(sqsValid s) 

,然後用

match SchemaQueryString tcs with 
| None ->() 
| Some(sql) -> ... // rest of function 
1

你可以用「扔」語句從您的代碼退出,但F#異常處理是比OCaml的一個更重的開始等功能(它不是F#quirk,它的.net功能)。