【问题标题】:Pattern Matching Types Out Of Strings in F#F# 中字符串中的模式匹配类型
【发布时间】:2015-08-06 14:32:57
【问题描述】:

我正在开发一个函数,该函数与我在 f# 中的一些用户定义类型匹配并将它们转换为字符串。部分代码如下所示:

let gsToString (gs : general_structure) : string = 
    match gs with
    | Date(Scattered(Eom(Ascending))) -> "Date(Scattered(Eom(Ascending)))"
    | Date(Scattered(Eom(SameDate(dt)))) -> "Date(Scattered(Eom(SameDate(" + dt.ToString() + "))))"
    | Number(AllNegative(Int1(Neither))) -> "Number(AllNegative(Int1(Neither)))" 
    | Number(AllNegative(Int1(SameInt(si)))) -> "Number(AllNegative(Int1(SameFloat(" + si.ToString() + "))))"

在这个函数中还有很多其他类型被匹配,但这些应该足以传达问题。此外,导致问题的类型是:

| SameDate of System.DateTime
| SameFloat of float

显然,执行第一个将我的 general_structure 类型转换为字符串的模式匹配函数非常简单。但是,在我的下一个函数(稍后需要在代码中调用)中出现了一个问题,我需要将字符串表示形式重新转换回 general_structure。问题区域如下所示:

let stringToGS (str : string) : general_structure = 
    match str with
    | "Date(Scattered(Eom(Ascending)))" -> Date(Scattered(Eom(Ascending)))
    | "Date(Scattered(Eom(SameDate(dt))))"-> Date(Scattered(Eom(SameDate(System.DateTime.Parse dt))))
    | "Number(AllNegative(Int1(Neither)))" -> Number(AllNegative(Int1(Neither))) 
    | "Number(AllPositive(Float1(SameFloat(sf))))" -> Number(AllPositive(Float1(SameFloat((float) sf)))) 

虽然 stringToGS 函数中的第一种和第三种情况工作得很好,但我无法找到将其他情况转换回其原始形式的方法。如果有任何方法可以在模式匹配语句中获取一个字符串(在这种情况下它将是 dt 和 fs)并以某种方式只解析模式的那部分以返回不同的值(在这种情况下,我试图让它们分别是 System.DateTimes 和 Floats),然后返回到它们的原始形式:

Date(Scattered(Eom(SameDate(dt))))
Number(AllPositive(Float1(SameFloat(sf))))

?我将不胜感激。

编辑:

我能够通过对导致问题的情况使用 if 语句执行以下操作来解决问题:

if str.Contains("Scattered(Eom(SameDate")
then 
    let p1 = str.IndexOf(")")
    let p2 = str.LastIndexOf("(")
    let dt1 = str.Remove(p1)
    let dt2 = dt1.Substring(p2 + 1)
    let date = System.DateTime.Parse dt2
    Date(Scattered(Eom(SameDate(date))))

然后,我可以对所有不包含嵌套数据的类型进行正常的模式匹配。

【问题讨论】:

  • 字符串格式是否需要与您的示例中的格式相同?如果不是,您也许可以使用一些现有的序列化库,例如:nessos.github.io/FsPickler
  • 是的,也考虑过 FsPickler - 但 AFAIK 没有包含类似 F# 语法的格式 - 当然有 XML 和 JSON
  • @Carsten 是的,我意识到这对我来说是一个非常愚蠢的想法。
  • 我会研究一些序列化
  • @AveryB 抱歉,这条评论可能是相当消极和粗鲁的 - 但也许如果你告诉我们你想用这个实现什么(你想保留数据吗?)我们可以给你更多积极的建议...

标签: f#


【解决方案1】:

如果类数量有限并且您不想使用序列化库,您也可以使用活动模式:

open System

let (|RegexMatch|_|) pattern input =
    let matches = System.Text.RegularExpressions.Regex.Matches(input, pattern)
    if matches.Count = 1 then Some matches.[0].Groups.[1].Value
    else None

type GeneralStructure =
| NoPayload
| DatePayload of DateTime
| StringPayload of string option

let toString = function
| NoPayload -> "NoPayload"
| DatePayload dt -> sprintf "DatePayload(%d)" <| dt.ToBinary()
| StringPayload None -> "StringPayload(None)"
| StringPayload (Some s) -> sprintf "StringPayload(Some(%s))" s

let fromString = function
| "NoPayload" -> NoPayload
| "StringPayload(None)" -> StringPayload None
| RegexMatch @"DatePayload\((.*)\)" dt -> DatePayload <| DateTime.FromBinary(Int64.Parse dt)
| RegexMatch @"StringPayload\(Some\((.*)\)\)" msg -> StringPayload <| Some msg
| o -> failwithf "Unknown %s %s" typeof<GeneralStructure>.Name o


let serialized = StringPayload <| Some "Foo" |> toString
let deserialized = fromString serialized

let serialized' = DatePayload DateTime.UtcNow |> toString
let deserialized' = fromString serialized'

// val serialized : string = "StringPayload(Some(Foo))"
// val deserialized : GeneralStructure = StringPayload (Some "Foo")
// val serialized' : string = "DatePayload(5247430828937321388)"
// val deserialized' : GeneralStructure = DatePayload 06.08.2015 18:04:10

请注意,正则表达式并非万无一失,我只是为了适应这些情况而编造的。

【讨论】:

    猜你喜欢
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 2011-12-17
    相关资源
    最近更新 更多