简体   繁体   English

F#如何编写一个带有int列表或字符串列表的函数

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

I'm messing around in F# and tried to write a function that can take an int list or a string list . 我在F#中乱搞并尝试编写一个可以采用int liststring list的函数。 I have written a function that is logically generic, in that I can modify nothing but the type of the argument and it will run with both types of list. 我编写了一个逻辑上通用的函数,因为我只能修改参数的类型,它将与两种类型的列表一起运行。 But I cannot generically define it to take both. 但我不能一般地定义两者。

Here is my function, without type annotation: 这是我的函数,没有类型注释:

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

When I try to annotate the function to take a generic list, I receive a warning FS0064: the construct causes the code to be less generic than indicated by the type annotations . 当我尝试注释函数以获取通用列表时,我收到警告FS0064: the construct causes the code to be less generic than indicated by the type annotations In theory I shouldn't need to annotate this to be generic, but I tried anyway. 从理论上讲,我不需要注释这是通用的,但无论如何我都试过了。

I can compile this in two separate files, one with 我可以在两个单独的文件中编译它,一个用

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

and another with 和另一个

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

In each of these files, compilation succeeds. 在每个文件中,编译成功。 Alternately, I can send the function definition and one of the tests to the interpreter, and type inference proceeds just fine. 或者,我可以将函数定义和其中一个测试发送给解释器,类型推断也可以正常进行。 If I try to compile, or send to the interpreter, the function definition and both tests, I receive error FS0001: This expression was expected to have type string, but here has type int . 如果我尝试编译,或发送到解释器,函数定义和两个测试,我收到error FS0001: This expression was expected to have type string, but here has type int

Am I misunderstanding how typing should work? 我是否误解了打字应该如何运作? I have a function whose code can handle a list of ints or a list of strings. 我有一个函数,其代码可以处理一个int列表或一个字符串列表。 I can successfully test it with either. 我可以用其中任何一个成功测试它。 But I can't use it in a program that handles both? 但我不能在一个处理两者的程序中使用它?

You can use inline to prevent the function from being fixed to a particular type. 您可以使用inline来防止将函数固定到特定类型。

In FSI, the interactive REPL: 在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

Note that the signature of contains5 has a generic element to it. 请注意,contains5的签名具有通用元素。 There's more about inline functions here . 还有更多的是内联函数在这里

You are running into value restrictions on the automatic generalization of the type inference system as outlined here 您正在对此处概述的类型推断系统的自动泛化遇到值限制

Specifically, 特别,

Case 4: Adding type parameters. 案例4:添加类型参数。

The solution is to make your function generic rather than just making its parameters generic. 解决方案是使您的函数通用,而不是仅仅使其参数通用。

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

You have to make the function inline because you have to use a statically resolved type parameter, and you have to use a statically resolved type parameter in order to use member constraints to specify that the type must be convertible to an int. 您必须使函数内联,因为您必须使用静态解析的类型参数,并且必须使用静态解析的类型参数才能使用成员约束来指定类型必须可转换为int。 As outlined here 作为概述这里

This is already answered correctly above, so I just wanted to chime in with why I think it's a good thing that F# appears to makes this difficult / forces us to lose type safety. 这已经在上面正确回答了,所以我只是想知道为什么我认为F#似乎使这很难/迫使我们失去类型安全是一件好事。 Personally I don't see these as logically equivalent: 我个人认为这些在逻辑上是等价的:

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

When inlined, the compiler would create two logically distinct versions of the function. 内联时,编译器将创建该函数的两个逻辑上不同的版本。 When performed on the list<int> we get a boolean result. 当在list<int>上执行时,我们得到一个布尔结果。 When performed on a list<string> we get a boolean result or an exception. 当在list<string>上执行时,我们得到一个布尔结果或一个异常。 I like that F# nudges me towards acknowledging this. 我喜欢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

Forces me to ask 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? 强迫我询问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? .

My approach above is horrible. 我上面的方法太可怕了。 I'd split the function that operates on the strings from the one that operates on the integers. 我将操作字符串的函数与对整数进行操作的函数分开。 I think your question was academic rather than a real use case though, so I hope I haven't gone off on too much of a tangent here! 我认为你的问题是学术问题,而不是一个真实的用例,所以我希望我在这里没有过多的切线!

Disclaimer: I'm a beginner, don't trust anything I say. 免责声明:我是初学者,不相信我说的任何话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM