尝试创建一个函数,然后在F#中通过“not that function”过滤序列

时间:2018-06-09 02:38:04

标签: f#

我的数据是SEQUENCE:

[(40,"TX");(48,"MO");(15,"TX");(78,"TN");(41,"VT")]

我的代码如下:

type Csvfile = CsvProvider<somefile>
let data = Csvfile.GetSample().Rows

let nullid row = 
    row.Id = 15

let otherid row =
    row.Id= 40

let iddata = 
   data
   |> Seq.filter (not nullid)
   |> Seq.filter (not otherid)

我创建了这些函数。

然后我想调用那些函数的“not”来过滤它们。

但问题是我在前两个函数中遇到“row.Id”错误,因为你只能用一个类型来做。

如何解决此问题,以便我能够成功完成此任务。

我的结果应该是以下序列:

[(48,"MO);(78,"TN");(41,"VT")]

2 个答案:

答案 0 :(得分:6)

您可以使用>>运算符来组合这两个函数:

let iddata = 
   data
   |> Seq.filter (nullid >> not)
   |> Seq.filter  (othered >> not)

请参阅Function Composition and Pipelining

或者你可以使它更明确:

let iddata = 
   data
   |> Seq.filter (fun x -> not (nullid x))
   |> Seq.filter  (fun x -> not (othered x))

您可以看到实际操作:

let input = [|1;2;3;4;5;6;7;8;9;10|];;
let is3 value =
    value = 3;;

input |> Seq.filter (fun x -> not (is3 x));;
input |> Seq.filter (not >> is3);;

他们都打印val it : seq<int> = seq [1; 2; 4; 5; ...]

答案 1 :(得分:1)

请参阅下面的MCVE在您的情况下可能会看到的内容,对于fsx文件,您可以使用Fsharp.Data引用#r dll,对于已编译的项目,只需引用dll即可打开它。

#if INTERACTIVE
#r @"..\..\SO2018\packages\FSharp.Data\lib\net45\FSharp.Data.dll"
#endif

open FSharp.Data

[<Literal>]
let datafile = @"C:\tmp\data.csv"
type CsvFile = CsvProvider<datafile>
let data = CsvFile.GetSample().Rows

最后,这是你想要实现的目标:

data
|> Seq.filter (fun x -> x.Id <> 15)
|> Seq.filter (fun x -> x.Id <> 40)
//val it : seq<CsvProvider<...>.Row> = seq [(48, "MO"); (78, "TN"); (41, "VT")]

执行此操作的一种方法是使用SRTP,因为它们允许进行结构类型化,其中类型取决于其形状,例如在这种情况下具有Id属性。如果需要,可以为两个数字15和40定义辅助函数,并在过滤器中使用它,就像在第二个示例中一样。然而,SRTP语法有点奇怪,它设计用于需要将函数应用于具有一些相似性的不同类型(基本上类似于接口)的用例。

let inline getId row =
    (^T : (member Id :  int) row)

data
|> Seq.filter (fun x -> (getId x <> 15 ))
|> Seq.filter (fun x -> (getId x <> 40))
//val it : seq<CsvProvider<...>.Row> = seq [(48, "MO"); (78, "TN"); (41, "VT")]

现在回到原来的帖子,因为你正确地指出你的函数会显示错误,因为你将它定义为通用的,但它需要在特定的Csv行类型(具有Id属性)上运行。这很容易修复,只需在row参数中添加类型注释即可。在这种情况下,您的类型为CsvFile.Row,并且由于CsvFile.Row具有Id属性,因此我们可以在函数中访问它。现在这个函数返回一个布尔值。你也可以让它返回实际的行。

let nullid (row: CsvFile.Row)  =
    row.Id = 15


let otherid (row: CsvFile.Row) =
    row.Id = 40

然后剩下的就是在Seq.filter中应用它并否定它:

let iddata = 
    data 
    |> Seq.filter (not << nullid) 
    |> Seq.filter (not << otherid)
    |> Seq.toList
//val iddata : CsvProvider<...>.Row list = [(48, "MO"); (78, "TN"); (41, "VT")]