簡體   English   中英

F#如何編寫一個帶有int列表或字符串列表的函數

[英]F# How to write a function that takes int list or string list

我在F#中亂搞並嘗試編寫一個可以采用int liststring list的函數。 我編寫了一個邏輯上通用的函數,因為我只能修改參數的類型,它將與兩種類型的列表一起運行。 但我不能一般地定義兩者。

這是我的函數,沒有類型注釋:

let contains5 xs =
    List.map int xs
    |> List.contains 5

當我嘗試注釋函數以獲取通用列表時,我收到警告FS0064: the construct causes the code to be less generic than indicated by the type annotations 從理論上講,我不需要注釋這是通用的,但無論如何我都試過了。

我可以在兩個單獨的文件中編譯它,一個用

let stringtest = contains5 ["1";"2";"3";"4"]

和另一個

let inttest = contains5 [1;2;3;4;5]

在每個文件中,編譯成功。 或者,我可以將函數定義和其中一個測試發送給解釋器,類型推斷也可以正常進行。 如果我嘗試編譯,或發送到解釋器,函數定義和兩個測試,我收到error FS0001: This expression was expected to have type string, but here has type int

我是否誤解了打字應該如何運作? 我有一個函數,其代碼可以處理一個int列表或一個字符串列表。 我可以用其中任何一個成功測試它。 但我不能在一個處理兩者的程序中使用它?

您可以使用inline來防止將函數固定到特定類型。

在FSI中,交互式REPL:

> open System;;
> let inline contains5 xs = List.map int xs |> List.contains 5;;
val inline contains5 :
  xs: ^a list -> bool when  ^a : (static member op_Explicit :  ^a -> int)

> [1;2;3] |> contains5;;
val it : bool = false

> ["1";"2";"5"] |> contains5;;
val it : bool = true

請注意,contains5的簽名具有通用元素。 還有更多的是內聯函數在這里

您正在對此處概述的類型推斷系統的自動泛化遇到值限制

特別,

案例4:添加類型參數。

解決方案是使您的函數通用,而不是僅僅使其參數通用。

let inline contains5< ^T when ^T : (static member op_Explicit: ^T -> int) > (xs : ^T list)  =
    List.map int xs
    |> List.contains 5

您必須使函數內聯,因為您必須使用靜態解析的類型參數,並且必須使用靜態解析的類型參數才能使用成員約束來指定類型必須可轉換為int。 作為概述這里

這已經在上面正確回答了,所以我只是想知道為什么我認為F#似乎使這很難/迫使我們失去類型安全是一件好事。 我個人認為這些在邏輯上是等價的:

let inline contains5 xs = List.map int xs |> List.contains 5

let stringTest = ["5.00"; "five"; "5"; "-5"; "5,"]
let intTest = [1;2;3;4;5]

contains5 stringTest // OUTPUT: System.FormatException: Input string was not in a correct format.
contains5 intTest // OUTPUT: true

內聯時,編譯器將創建該函數的兩個邏輯上不同的版本。 當在list<int>上執行時,我們得到一個布爾結果。 當在list<string>上執行時,我們得到一個布爾結果或一個異常。 我喜歡F#推動我承認這一點。

let maybeInt i = 
    match Int32.TryParse i with
    | true,successfullyParsedInteger -> Some successfullyParsedInteger
    | _ -> None

let contains5 xs = 
    match box xs with
    | :? list<int> as ixs -> 
        ixs |> List.contains 5 |> Ok
    | :? list<string> as sxs -> 
        let successList = sxs |> List.map maybeInt |> List.choose id
        Ok (successList |> List.contains 5)
    | _ -> 
        Error "Error - this function expects a list<int> or a list<string> but was passed something else."

let stringTest = ["5.00"; "five"; "5"; "-5"; "5,"]
let intTest = [1;2;3;4;5]

let result1 = contains5 stringTest // OUTPUT: Ok true
let result2 = contains5 intTest // OUTPUT: Ok true

強迫我詢問if some of the values in the string list cannot be parsed, should I drop out and fail, or should I just try and look for any match on any successful parse results?

我上面的方法太可怕了。 我將操作字符串的函數與對整數進行操作的函數分開。 我認為你的問題是學術問題,而不是一個真實的用例,所以我希望我在這里沒有過多的切線!

免責聲明:我是初學者,不相信我說的任何話。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM