我想这样做,如果作为参数传递的列表是一个浮点列表,而不是其他任何东西,那么返回浮点数List的平均值(作为一个浮点数)整数列表。它应该只计算浮点数列表的平均值。
所以说我传递一个int列表,然后它应该返回none语句。或者如果我传递一个空列表,它也应该返回none语句。
let avg x =
let average = List.averageBy (fun elem -> float elem) x
match List.tryFind x with
|Some(x) -> printfn "The average of the list is %f" average
|None -> "There are no float elements in the list, hence no average"
avg [1.0;5.0;6.0;10.0]
得到错误FS0001:这个表达式应该有''a-> bool'类型,但这里有''b list'类型。
哪个有道理。但是,我无法弄清楚在哪里给它一个bool值。
修改
我正在考虑使用List.forall检查列表是否包含浮点数。仍然遇到同样的BOOL问题。
答案 0 :(得分:4)
以下假设您传递了不同类型的列表,我假设您是因为您说您必须使用List
模块中的函数。
let average list =
let isFloat (x : obj) =
match x with
| :? float -> true
| _ -> false
let toFloat (x : obj) = x :?> float
if List.forall isFloat list then
List.averageBy toFloat list
|> Some
else None
您收到编译错误的原因是因为List.tryFind
使用不正确,但这不是完整的故事。正确使用tryFind
看起来像
let findFirstEven list = List.tryFind (fun x -> x % 2 = 0) list
F#是强类型的,这意味着一般,我们希望您不要尝试将多种类型传递给函数。函数期望每个参数只有一种类型,传递不同的东西是编译时错误。
也就是说,因为F#是基于.NET构建的并且支持类和继承,所以函数可以接受一个类,但是如果实际传递的对象是预期的一个派生类,它可以做不同的事情。 。在F#中,类型强制测试是这样完成的:
type Person (name) =
member this.Name : string = name
type Student (name, university) =
inherit Person (name)
member this.University : string = university
type Adult (name, job) =
inherit Person (name)
member this.Job : string = job
let greet (person : Person) =
match person with
| :? Student as student -> printfn "Hello, %s of %s" student.Name student.University
| :? Adult as adult -> printfn "Hello, %s. I hope you enjoy working at %s!" adult.Name adult.Job
| _ -> printfn "Nice to meet you, %s" person.Name
这仅适用于派生类型,而不适用于真正的F#泛型类型,例如尝试将'T list
与float list
或其他任何内容匹配。但是,.NET的结构方式意味着每种可能的类型都是obj
,或者来自obj
。这意味着如果我们允许传递任何可能的类型,我们可以测试它是否为float list
。
let average (value : obj) =
match value with
| :? List<float> as list -> List.average list |> Some
| _ -> None
这个函数可以用字面上的任何可表达的类型,所以我们真的真正试图摆脱F#中的写作。相反,我们可以保证您可以保证您将被传递一系列事物,但事物可能是彼此不同的类型。同样,这是我们在编写F#时必须避免的情况,但是现在:
let average list =
let isFloat (x : obj) =
match x with
| :? float -> true
| _ -> false
let toFloat (x : obj) = x :?> float
if List.forall isFloat list then
List.averageBy toFloat list
|> Some
else None
这会单独测试列表中的每个元素是否为float
,然后如果是,则将列表元素强制转换为float
并对其进行平均。需要向下:?>
,因为否则List.average
不知道如何执行平均值(它仍然认为列表可能是任何内容),但它也是一个危险的运算符使用不慎。如果对派生类型的强制转换失败,则代码将抛出异常,这可能会终止您的程序。在这里,我们知道它是安全的,因为我们只测试了相同的演员阵容,但您应该避免在代码中使用它,除非您确定它是安全的。即使这样,这对于惯用的F#代码也没有用,因为我们主要希望你使用真正的泛型来正确使用类型系统。
答案 1 :(得分:2)
这是应该有用的东西:
let avg (arg : obj) =
match arg with
:? list<float> as xs -> Some (List.average xs)
| _ -> None
或(同样的,另一种写作方式):
let avg : obj -> _ = function
:? list<float> as xs -> Some (List.average xs)
| _ -> None