F#中对象的匹配模式

时间:2016-05-10 13:26:39

标签: f# f#-3.0

我对F#没有经验。我本周末开始阅读F# for C# Developers

我最近遇到了一个问题(C#Code),我必须在.NET对象列表中搜索匹配某种模式的对象子集。

我把它描述为"正则表达式匹配对象"在问题中。我确实得到了一个很好的解决方案,我很满意,而且看起来效果很好。

但是我最近对F#的兴趣让我想知道函数式编程是否可能有更好的解决方案。

你如何在F#中解决这个问题?

Regex Style Pattern Matching in .NET Object Lists

3 个答案:

答案 0 :(得分:4)

这里可能有用的一个有趣的功能概念是parser combinators。我们的想法是使用可组合函数来描述如何读取一些输入,然后从一些基元组成复杂模式的解析器。

解析器通常在字符串上工作,但没有理由不能使用相同的方法来读取染色体序列。

使用解析器组合器,您可以重写"正则表达式"作为F#函数的组合。类似的东西:

sequence [ 
  zeroOrMore (chromosomeType R)
  repeat 3 (chromosomeType B)
  zeroOrMore (chromosomeType D) ]

这将为您提供一个函数,该函数获取染色体对象列表并返回解析结果 - 列表的子集。我们的想法是像zeroOrMore这样的函数构建解析器来检测某些模式并组合函数来构建解析器 - 你可以在输入上运行一个函数来解析它。

解释解析器组合器对于SO答案来说有点太长了,但它可能是解决问题的最惯用的F#方法。

答案 1 :(得分:2)

这是一个评论作为答案发布,因为它是为了评论。

由于正则表达式似乎正在解决您的问题,并且您可能怀疑F#pattern matchingactive patterns可能是更好的解决方案,因此我会添加一些理解。

我认为输入R+B{3}D+formal grammar,因此需要解析器和评估引擎,或者创建像有限状态机这样的东西。既然你已经知道.Net Regex可以解决这个问题,为什么要经历所有麻烦来用F#重新创建它。即使使用F#模式匹配和活动模式也不会比使用RegEx更容易。

因此,问题基本上是将C#代码转换为F#代码并使用RegEx。因此,您要求我们将您的C#翻译为F#,这不是一个有效的SO问题。

修改

正如Mark Seemann在评论中指出的那样,我们唯一的输入是R+B{3}D+。因此,如果您的实际语法比RegEx可以处理的语法更复杂,那么可能是F#中更好的解决方案。

希望这可以帮助您了解您的要求。

答案 2 :(得分:2)

基于其他答案:首先实现一个解析器组合器,允许组合Parser<'a list>类型。添加解析器以获得R+B{3}D+所需的最小语法。

type Result<'T> = Success of 'T | Failure
type Parser<'T> = Parser of ('T -> Result<'T * 'T>)

module ListParser =
    let (.>>.) (Parser f1) (Parser f2) =
        Parser <| fun input ->
            match f1 input with
            | Failure -> Failure
            | Success(value1, rest1) ->
                match f2 rest1 with
                | Failure -> Failure
                | Success(value2, rest2) -> 
                    Success(value1 @ value2, rest2)

    let oneOrMore what =
        let rec aux gotOne acc = function
        | x::xs when what = x -> aux true (x::acc) xs
        | xss when gotOne -> Success(List.rev acc, xss)
        | _ -> Failure
        Parser <| aux false []

    let exactly what n =
        let rec aux i acc = function
        | xss when i = 0 -> Success(List.rev acc, xss)
        | x::xs when what = x -> aux (i - 1) (x::acc) xs
        | _ -> Failure
        Parser <| aux n []

最后创建一个重复运行解析器的函数,直到输入列表用完为止。

open ListParser
let runForall (Parser f) xss =
    let rec aux n acc xss =
        match xss, f xss with
        | [], _ -> List.rev acc
        | _::xs, Failure -> aux (n + 1) acc xs
        | _, Success(value, rest) ->
            aux (n + List.length value) ((n + 1, value)::acc) rest
    aux 0 [] xss

type ChromosomeType = R | B | D

[D;R;R;B;B;B;D;D;B;R;R;B;B;B;D;D;R;R;B;B;B;D;D]
|> runForall (oneOrMore R .>>. exactly B 3 .>>. oneOrMore D)
// val it : (int * ChromosomeType list) list =
//   [(2, [R; R; B; B; B; D; D]); (10, [R; R; B; B; B; D; D]);
//    (17, [R; R; B; B; B; D; D])]