我对F#没有经验。我本周末开始阅读F# for C# Developers。
我最近遇到了一个问题(C#Code),我必须在.NET对象列表中搜索匹配某种模式的对象子集。
我把它描述为"正则表达式匹配对象"在问题中。我确实得到了一个很好的解决方案,我很满意,而且看起来效果很好。
但是我最近对F#的兴趣让我想知道函数式编程是否可能有更好的解决方案。
你如何在F#中解决这个问题?
答案 0 :(得分:4)
这里可能有用的一个有趣的功能概念是parser combinators。我们的想法是使用可组合函数来描述如何读取一些输入,然后从一些基元组成复杂模式的解析器。
解析器通常在字符串上工作,但没有理由不能使用相同的方法来读取染色体序列。
使用解析器组合器,您可以重写"正则表达式"作为F#函数的组合。类似的东西:
sequence [
zeroOrMore (chromosomeType R)
repeat 3 (chromosomeType B)
zeroOrMore (chromosomeType D) ]
这将为您提供一个函数,该函数获取染色体对象列表并返回解析结果 - 列表的子集。我们的想法是像zeroOrMore
这样的函数构建解析器来检测某些模式并组合函数来构建解析器 - 你可以在输入上运行一个函数来解析它。
解释解析器组合器对于SO答案来说有点太长了,但它可能是解决问题的最惯用的F#方法。
答案 1 :(得分:2)
这是一个评论作为答案发布,因为它是为了评论。
由于正则表达式似乎正在解决您的问题,并且您可能怀疑F#pattern matching或active 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])]