【问题标题】:Am I properly using function composition?我是否正确使用功能组合?
【发布时间】:2010-09-01 01:33:54
【问题描述】:

为了了解函数式编程的功能,我将一些基本函数放在一起,您可以将它们组合在一起以构建复杂的正则表达式。现在经过一些测试,我发现这是可行的,但是您可以用任何可行的语言编写一些可怕的代码。这是您会找到专业 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# functional-programming function-composition


    【解决方案1】:

    代码对我来说看起来不错。

    我不确定这是您的意图还是巧合,但您正在实现与“解析器组合器”非常相似的东西,这是许多学术论文的主题:-)。我认为Monadic Parser Combinators 可读性很强(它在 Haskell 中有示例,但您应该能够将它们翻译成 F#)。

    关于函数组合运算符。我一般不喜欢过多地使用运算符,因为它经常混淆代码。但是,在您的示例中它很有意义,因为您可以很容易地想象 &gt;&gt; 的意思是“这个组应该被那个组跟随”,这很容易解释。

    我要做的唯一小改动是为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
    

    这是更高级的描述,因此它删除了一些&gt;&gt; 运算符,以支持更具描述性的函数(并封装&gt;&gt;)。我还更改了quantify 以采用多个参数而不是三倍(这是一个小改动)

    如果你想进一步玩这个,那么你可以看看这篇文章并尝试编写 F# 计算表达式构建器,让你可以使用parser { .. } 语法。

    【讨论】:

    • 很高兴知道我的函数式编程技能正在取得进步。您让我非常兴奋地尝试将所有内容包装到优雅的计算表达式语法中。 :) 无论如何感谢您的建议和论文(我是 Erik Meijer 的任何事物的粉丝。).
    【解决方案2】:

    这通常是不错的风格,但是您缺少一些技巧并且仍然有相当多的冗余。也许更像这样:

    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"
    

    您可能更喜欢累积一个表示计算的值而不是闭包,因为它使调试更容易。

    【讨论】:

    • 哇,我没有想到 valid 功能感到有点愚蠢。感谢您的建议。
    猜你喜欢
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    • 2014-01-01
    • 2017-01-30
    • 1970-01-01
    • 2016-01-24
    • 2014-05-15
    • 2010-12-31
    相关资源
    最近更新 更多