【问题标题】:Matching More than One ParserResult and Extracting Values匹配多个 ParserResult 并提取值
【发布时间】:2020-12-15 15:36:45
【问题描述】:

这是一个关于使用 FParsec 的 ParserResult 的问题。

是否有更简洁的match_result 实现来提取ParserResult 中包含的XKEYVALUEXATTRIBUTES,而无需两个嵌套匹配?

以下代码用作 F# 控制台应用程序...

// Learn more about F# at http://fsharp.org

open System
open FParsec

// Types
type XKEYVALUE = string * string
type XATTRIBUTES = Map< string, string>

type PARSER_RESULT_XML = 
    | PR_XKEYVALUE of ParserResult<XKEYVALUE, unit>
    | PR_XATTRIBUTES of ParserResult<XATTRIBUTES, unit>

// Parser Trace
let (<!>) (p: Parser<_,_>) label : Parser<_,_> =
    fun stream ->
        printfn "%A: Entering %s" stream.Position label
        let reply = p stream
        do
            match (reply.Status) with
                | Ok  -> printfn "%A: Leaving %s (%A) - %A" stream.Position label reply.Status reply.Result
                | Error -> printfn "%A: Leaving %s (%A) - %A" stream.Position label reply.Status reply.Error
                | FatalError -> printfn "%A: Leaving %s with FatalError (%A)" stream.Position label reply.Status
                | _ -> printfn "%A: Leaving %s with unknown status" stream.Position label
        reply

// Parsers
let ws = spaces
let str = pstring
let xKey : Parser<string,unit> = (ws >>. regex "[a-zA-Z][a-zA-Z0-9:]*") <!> "xKey"
let xStringLiteral = regex "[^\"]+" <!> "xStringLiteral"
let xKeyValue : Parser<XKEYVALUE, unit> = 
    ws >>. xKey .>>. (ws >>. str "=" >>. ws >>. str "\"" >>. xStringLiteral .>> str "\"" .>> ws) |>> XKEYVALUE <!> "xKeyValue"
let xAttributes : Parser<XATTRIBUTES, unit> = sepEndBy xKeyValue ws |>> XATTRIBUTES <!> "xAttributes"

// Test Data
let xKeyValue_text = """    key =  "value"    aa"""
let xAttributes_text = "key1=\"value1\" key2=\"value2\""

let match_result ( result : PARSER_RESULT_XML) : string =
    match result with 
        | PR_XKEYVALUE(a) ->    match a with  
                                    | Success( ((key : string), (value : string)), x2, x3) -> sprintf "%s=\"%s\"" key value
                                    | Failure( _, _, _) -> sprintf "FAILURE"
        | PR_XATTRIBUTES( a) -> match a with
                                    | Success( (x1 : Map<string, string>), x2, x3) -> sprintf "x1=%A, x2=%A, x3=%A" x1 x2 x3
                                    | Failure( _, _, _) -> sprintf "FAILURE"

[<EntryPoint>]
let main argv =
    run xKeyValue xKeyValue_text |> PR_XKEYVALUE |> match_result |> printfn "%A"
    run xAttributes xAttributes_text |> PR_XATTRIBUTES |> match_result |> printfn "%A"
    0 // return an integer exit code

但是 match_result 的嵌套匹配看起来很笨拙。

一个失败的实验是使用AND pattern matchPARSER_RESULT_XML &amp; Success( …) 的匹配放在同一个匹配表达式中,但我无法让两个匹配表达式的类型一致。

您将如何修改 match_result 以使其更好或更简洁?

【问题讨论】:

  • 代码无法编译。
  • 代码会编译并运行,但在 Visual Studio Pro 版本 16.8.2 中使用默认设置会出现警告。 VS Pro 编译器会抛出三个警告:2 x“此表达式的不完整模式匹配...”用于第 40 和 43 行,以及 1 x“永远不会达到此规则”。对于第 46 行。显然,您的编译器被配置为不编译给定这些警告。我已经删除了警告并替换了代码。感谢您的评论,这样我就可以让其他人更轻松..

标签: f# fparsec


【解决方案1】:

您可以根据需要将模式匹配到结构中的深度:

let match_result result =
    match result with
    | PR_XKEYVALUE (Success ((key, value), _, _)) -> sprintf "%s=\"%s\"" key value 
    | PR_XATTRIBUTES (Success (x1, x2, x3)) -> sprintf "x1=%A, x2=%A, x3=%A" x1 x2 x3
    | _ -> "FAILURE"

【讨论】:

  • 最初,我尝试过这个,但是当我学习 F# 时,我可能对另一个错误感到困惑,我认为这是由于我尝试了深度模式匹配。感谢您向我展示这确实有效。太棒了!
【解决方案2】:

这对我来说是主观的,但如果你去掉不需要的类型注释并更改 match_result 上的缩进,我认为它会稍微提高可读性。

let match_result result =
    match result with 
    | PR_XKEYVALUE a ->
        match a with  
        | Success ((key, value), _, _) -> sprintf "%s=\"%s\"" key value
        | Failure _ -> sprintf "FAILURE"
    | PR_XATTRIBUTES a -> 
        match a with
        | Success (x1, x2, x3) -> sprintf "x1=%A, x2=%A, x3=%A" x1 x2 x3
        | Failure _ -> sprintf "FAILURE"

如果您仍然对此不满意,您可能会发现在这里使用主动模式很有帮助:

let (|KeyValue|Attributes|Failed|) result =
    match result with
    | PR_XKEYVALUE a ->
        match a with  
        | Success ((key, value), _, _) -> KeyValue (key, value)
        | Failure _ -> Failed
    | PR_XATTRIBUTES a -> 
        match a with
        | Success (x1, x2, x3) -> Attributes (x1, x2, x3)
        | Failure _ -> Failed

let match_result result =
    match result with 
    | KeyValue (key, value) -> sprintf "%s=\"%s\"" key value
    | Attributes (x1, x2, x3) -> sprintf "x1=%A, x2=%A, x3=%A" x1 x2 x3
    | Failed -> sprintf "FAILURE"

后者更好,因为您可以分离以域术语解释结果,以及如何处理它(在本例中为打印消息)。

【讨论】:

  • 谢谢吉姆。我喜欢你使用主动模式来避免使用类型构造函数。
猜你喜欢
  • 2015-04-25
  • 2022-10-25
  • 2017-08-09
  • 1970-01-01
  • 2021-10-31
  • 2020-01-12
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
相关资源
最近更新 更多