我是否正确使用函数组合?
-
26-09-2019 - |
题
在努力了解函数式编程我把一些基本的功能结合在一起,可以组成共同打造复杂的正则表达式的能力。现在一些测试后,我发现这个作品,但你可以将工作的任何语言编写一些可怕的代码。这是你会发现一个专业的F#程序员写还是我滥用特征样的代码?
注:的test
是具体是什么,我指的是
type State = { input:string; index:int; succeeded:bool }
type Matcher = State -> State
let term (cs:char Set) =
fun s ->
if s.succeeded && s.index < s.input.Length && cs.Contains s.input.[s.index] then
{ input = s.input; index = s.index + 1; succeeded = true }
else
{ input = s.input; index = s.index; succeeded = false }
let quantify (term, min, max) =
let rec inner (s:State, count) =
if s.succeeded && s.index < s.input.Length && count <= max then
inner (term { input = s.input; index = s.index + 1; succeeded = true }, count + 1)
elif count >= min && count <= max then
{ input = s.input; index = s.index - 1; succeeded = true }
else
s
fun s -> inner (s, 0)
let disjunction leftTerm rightTerm =
fun s ->
let left = leftTerm s
if not left.succeeded then
let right = rightTerm s
if not right.succeeded then
{ input = s.input; index = s.index; succeeded = false }
else
right
else
left
let matcher input terms =
let r = terms { input = input; index = 0; succeeded = true }
if r.succeeded then r.input.Substring (0, r.index) else null
let test = // (abc|xyz)a{2,3}bc
disjunction // (abc|xyz)
(term (set "a") >> term (set "b") >> term (set "c"))
(term (set "x") >> term (set "y") >> term (set "z"))
>> quantify (term (set "a"), 2, 3) // (a{2,3})
>> term (set "b") // b
>> term (set "c") // c
let main () : unit =
printfn "%s" (matcher "xyzaabc" test)
System.Console.ReadKey true |> ignore
main()
解决方案
中的代码看起来相当不错。
我不知道这是否是你的意图还是巧合,但你实现神似“解析器组合”的东西,这是许多学术论文:-)的话题。我认为,单子解析器组合是相当可读(它有实例哈斯克尔,但你应该能够将其转换为F#)。
关于函数组合运算符。我一般不使用运营商太多,因为它常常混淆了代码的大风扇。然而,在你的榜样它使一个良好的感觉,因为你可以很容易地想象>>
手段“这一群体应遵循该组”,这是很容易解释的。
在只有轻微的变化,我会做的是选择适合的disjunction
操作一些不错的运营商定制,并确定了几个基本的操作,这样就可以,例如写:
// Test against several terms in sequence
let sequence terms = (fun state -> terms |> Seq.fold (>>) state)
// Test for a substring
let substring s = sequence [ for c in s -> term (set [c]) ]
let test = // (abc|xyz)a{2,3}bc
( substring "abc" <|> substring "xyz" )
>> quantify 2 3 (term (set "a")) // (a{2,3})
>> substring "bc" // bc
这是更更高层次的描述,所以它去除了一些>>
运营商支持函数是更具描述性的(和封装>>
)的。我也改变quantify
采取多个参数,而不是一个特里普尔(这是一个微小的变化)
如果你想用这个进一步的发挥,那么你可以看看文章,并尝试写F#计算表达式生成器,将允许您使用parser { .. }
语法。
其他提示
这总体上是好的风格,但你错过了一些技巧,仍然有相当多的冗余。也许更是这样的:
let valid (s: State) = s.succeeded && s.index < s.input.Length
...
let disjunction leftTerm rightTerm s =
let left = leftTerm s
if left.succeeded then left else
let right = rightTerm s
if right.succeeded then right else
{ s with succeeded = false }
...
let test =
let f s = set s |> term
let (++) s t = f s >> f t
disjunction ("a" ++ "b" ++ "c") ("x" ++ "y" ++ "z")
>> quantify (f "a", 2, 3)
>> "b" ++ "c"
您可能更喜欢积累的值代表一个计算,而不是封锁,因为它使调试变得更加容易。
不隶属于 StackOverflow