2016-01-07 82 views
3

我想鞏固下面幾行:如何將兩個函數調用合併爲一個?

let result1 = add (numbers, ",") 
let result2 = add (numbers, "\n") 

弄成這個樣子:

let resultX = add (numbers, ",") |> add (numbers, "\n") 

我可以撰寫功能也是這樣嗎?

注:

我學習F#和,如果這個問題似乎是愚蠢的道歉。

的代碼如下:

module Calculator 

open FsUnit 
open NUnit.Framework 
open System 

let add (numbers:string) = 

    let add (numbers:string) (delimiter:string) = 
     if (numbers.Contains(delimiter)) then 
      numbers.Split(delimiter.Chars(0)) |> Array.map Int32.Parse 
               |> Array.sum 
     else 0 

    let result1 = add numbers "," 
    let result2 = add numbers "\n" 

    if (result1 > 0 || result2 > 0) then 
     result1 + result2 

    else let _ , result = numbers |> Int32.TryParse 
     result 

測試:

[<Test>] 
let ``adding empty string returns zero``() = 

    let result = add "" 
    result |> should equal 0 

[<Test>] 
let ``adding one number returns number``() = 

    let result = add "3" 
    result |> should equal 3 

[<Test>] 
let ``add two numbers``() = 

    let result = add "3,4" 
    result |> should equal 7 

[<Test>] 
let ``add three numbers``() = 

    let result = add "3,4,5" 
    result |> should equal 12 

[<Test>] 
let ``line feeds embedded``() = 

    let result = add "3\n4" 
    result |> should equal 7 

修訂

我收到以下錯誤:

The type 'int' does not match the type 'string'

let add (numbers:string) = 

    let add (numbers:string) (delimiter:string) = 
     if (numbers.Contains(delimiter)) then 
      numbers.Split(delimiter.Chars(0)) |> Array.map Int32.Parse 
               |> Array.sum 
     else 0 

let resultX = numbers |> add "," 
         |> add "\n" 

實現反饋:

let add (numbers:string) = 

    let add (numbers:string) (delimiters:char array) = 
     if numbers.Length = 0 then 0 
     else numbers.Split(delimiters) |> Array.map Int32.Parse 
             |> Array.sum 
    let delimiters = [|',';'\n'|] 
    add numbers delimiters 
+1

您是否要求每個分隔符都是通過單獨調用add函數完成的?你想了解函數組合還是試圖對字符串求和?如果你試圖總結字符串,我建議你做一個調用將所有分隔符作爲數組傳遞給[Split](https://msdn.microsoft.com/en-us/library/b873y76a(v = vs .110).aspx)方法。 –

+0

我們應該提出有關「類型'int'與類型'string'不匹配的問題」一個單獨的問題。如果你可以問它,我可以把答案的那部分移到它。這實際上是兩個問題,另一部分需要更容易找到。 –

回答

3

這不是一個確切的答案,因爲我不知道你的意思,但它應該給你一些想法。

let add01 (numbers:string) = 
    let delimiters : char array = [|',';'\n'|] 
    let inputArray : string array = numbers.Split(delimiters) 
    let numbers : string list = Array.toList(inputArray) 
    let rec add (numbers : string list) (total : int) : int = 
     match (numbers : string list) with 
     | ""::t -> 
      add t total 
     | h::t -> 
      let number = System.Int32.Parse h 
      let total = total + number 
      add t total 
     | [] -> total   
    add numbers 0 

let numbers = "1,2,3\n4,5,6\n\n" 
let result = add01 numbers 

當給出以下代碼時會出現以下錯誤,爲什麼?

// Type mismatch. Expecting a 
//  int -> 'a  
// but given a 
//  string -> int  
// The type 'int' does not match the type 'string' 
let result = numbers |> add "," 
        |> add "\n" 

因爲這是一個錯誤,指出兩種不同意一個需要理解type inferencing以及如何解決這些問題。 我不會在這裏解釋類型推論,因爲這本身就是一個很大的主題,但是我會給出一個模式的例子,這個模式在我解決這些錯誤的過程中大部分時間都是成功的。

當F#編譯代碼時,它使用類型推斷在類型檢查之前將缺少的類型添加到函數和值中,並且它是類型檢查失敗。因此,爲了看到編譯器看到的類型,我們將在這裏手動添加它們,並將不會導致問題的代碼部分分解出來,從而導致出現錯誤的原因,然後很明顯需要修復。

有類型的唯一的事情是:

  • 結果
  • =
  • 數字
  • |>
  • 添加
  • 「」
  • 「\ n」

類型的價值觀很簡單:

  • 結果:整數
  • 數:字符串
  • 「」:字符串
  • 「\ n」:字符串

我不不要回想F#將等號(=)當作一個函數,但這裏是如何考慮它。
=: 'A - >' 一個

管道運算符

let (|>) (x : 'a) f = f (x : 'a) 

爲了解決這個問題只是覺得管道運營商作爲syntactic sugar
請參閱下面的示例以獲得更好的理解。

add函數
地址:字符串 - >字符串 - > INT

所以,讓我們細化誤差降低到它的本質。

//Type mismatch. Expecting a 
// int -> 'a  
//but given a 
// string -> int  
//The type 'int' does not match the type 'string' 
let result = numbers |> add "," 
        |> add "\n" 

將類型簽名添加到值並驗證我們得到相同的錯誤。 這是什麼類型的推理會做,我們手動。

//Type mismatch. Expecting a 
// int -> int  
//but given a 
// string -> int  
//The type 'int' does not match the type 'string' 
let (result : int) = (numbers : string) |> add ("," : string) 
        |> add ("\n" : string) 

現在將代碼看作可以分解的數學表達式。

分解第一個管道運算符並驗證我們得到相同的錯誤。 意識到錯誤現在只有R2

的一部分
//Expecting a 
// int -> 'a  
//but given a 
// string -> int  
//The type 'int' does not match the type 'string' 
let (result : int) = 
    let r1 = (numbers : string) |> add ("," : string) 
    let r2 = r1 |> add ("\n" : string) 
    r2 

撤消語法糖第二管道運營商,並確認我們得到了同樣的錯誤。 注意錯誤現在只是r2的一部分;特別是r1參數

//This expression was expected to have type 
// string  
//but here has type 
// int 
let (result : int) = 
    let r1 = (numbers : string) |> add ("," : string) 
    let r2 = add ("\n" : string) r1 
    r2 

將該類型添加到r1並驗證我們得到相同的錯誤。

//This expression was expected to have type 
// string  
//but here has type 
// int 
let (result : int) = 
    let (r1 : int) = (numbers : string) |> add ("," : string) 
    let r2 = add ("\n" : string) r1 
    r2 

此時錯誤應該是顯而易見的。 第一個管道運算符的結果是int並作爲第二個參數傳遞給add函數。 add函數的第二個參數需要string,但是被賦予了int


爲了更好地理解管道操作員的工作原理,我爲此演示創建了一個等效的用戶定義操作符。

這些是演示的一些幫助函數。

let output1 w = 
    printfn "1: %A" w 

let output2 w x = 
    printfn "1: %A 2: %A" w x 

let output3 w x y = 
    printfn "1: %A 2: %A 3: %A" w x y 

let output4 w x y z = 
    printfn "1: %A 2: %A 3: %A 4: %A" w x y z 

使用沒有管道運算符的輸出函數。

output1 "a" 
1: "a" 

output2 "a" "b" 
1: "a" 2: "b" 

output3 "a" "b" "c" 
1: "a" 2: "b" 3: "c" 

output4 "a" "b" "c" "d" 
1: "a" 2: "b" 3: "c" 4: "d" 

請注意,輸出與輸入順序相同。

在管道運算符中使用輸出函數。

//讓(|>),因爲使用的XF = FX

"a" |> output1 
1: "a" 

"a" |> output2 "b" 
1: "b" 2: "a" 

"a" |> output3 "b" "c" 
1: "b" 2: "c" 3: "a" 

"a" |> output4 "b" "c" "d" 
1: "b" 2: "c" 3: "d" 4: "a" 

注意,對於輸出功能的最後一個參數是在管道運算符(「A」)的左邊的值管道運營商(|>)。

//有關如何定義用戶定義的運算符,請參閱F# specification的第3.7節。

將輸出函數與用戶定義的管線運算符一起使用。

let (@.) x f = f x 

"a" @. output1 
1: "a" 

"a" @. output2 "b" 
1: "b" 2: "a" 

"a" @. output3 "b" "c" 
1: "b" 2: "c" 3: "a" 

"a" @. output4 "b" "c" "d" 
1: "b" 2: "c" 3: "d" 4: "a" 
+0

你回答了我的問題的意圖。這是分隔符的值。 –

+0

我不確定你的學習重點是什麼,但很高興你今天學到了一些東西。花點時間閱讀擴展的答案,這將是你會定期回來的。 –

3

我不知道的撰寫功能的任何普遍的方式就像你似乎在問,但如果你只需要改變一個參數,一個選擇是創建參數列表,然後在地圖那些:

let results = [","; "\n"] |> List.map (add numbers) 

如果你這樣做,那麼resultsint list,然後你需要決定如何處理該名單做。在這種情況下,對列表進行總結似乎是合適的,但考慮到目前的條件檢查result1result2是否是肯定的,這似乎不合適。


所有這些都說,考慮到當前提供的測試用例,沒有理由讓它比它更復雜。這種實現還通過了所有測試:

let add = 
    let split (x : string) = 
     x.Split([| ','; '\n' |], StringSplitOptions.RemoveEmptyEntries) 
    split >> Array.map Int32.Parse >> Array.sum 

這是不是一個特別強大的執行情況,如果字符串包含不能被解析成整數的字符,它會失敗,但因此將OP執行。

+0

謝謝馬克。你剛剛在我的邏輯中發現了一個大於零的錯誤。 –