过滤并将`list option`转换为`list`?

时间:2013-12-13 00:09:06

标签: f#

我有以下代码,这些代码会为这些可以解析的Url返回seq DownloadLink

type DownloadLink = { Url: string; Period: DateTime }   

nodes |> Seq.map (fun n ->
    let url = n.Attributes.["href"].Value
    match url with
    | Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
        { Url = url; Period = period }
    | _ ->
        printfn "Cannot parse %s" url // Error
        )

但是,我在printfn收到了以下错误。什么是正确的实施方式?我应先将list option设为None,然后过滤掉这些{{1}}项吗?

Error   1   Type mismatch. Expecting a
    string -> DownloadLink    
but given a
    string -> unit    
The type 'DownloadLink' does not match the type 'unit'  

2 个答案:

答案 0 :(得分:5)

基本问题是,如果你有像

这样的东西
match x with
|true -> A
|false -> B

AB的类型必须相同。

实际上有一个内置函数,它使用Some组合了地图和过滤器 - 使用Seq.choose就像这样

nodes |> Seq.choose (fun n ->
    let url = n.Attributes.["href"].Value
    match url with
    | Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
        Some ({ Url = url; Period = period })
    | _ ->
        printfn "Cannot parse %s" url // Error
        None
        )

答案 1 :(得分:4)

Seq.choose之外,您还可以使用序列表达式很好地解决问题 - 您可以使用yield在一个分支中返回结果,但不必在另一个分支中生成值:

seq { for n in nodes do
        let url = n.Attributes.["href"].Value
        match url with
        | Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
            yield { Url = url; Period = period }
        | _ ->
            printfn "Cannot parse %s" url }

除此之外,我不建议将副作用(打印)作为处理代码的一部分。如果要报告错误,最好返回一个选项(或定义SuccessError of string类型),以便将错误报告与处理分开。