代码之家  ›  专栏  ›  技术社区  ›  ChaosPandion

我是否正确使用函数组合?

  •  7
  • ChaosPandion  · 技术社区  · 14 年前

    为了理解函数编程的功能,我把一些基本函数放在一起,您可以将它们组合在一起以构建复杂的正则表达式。现在,经过一些测试,我发现这是可行的,但是您可以用任何可行的语言编写一些可怕的代码。这是你会发现一个专业的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()
    
    2 回复  |  直到 14 年前
        1
  •  8
  •   Tomas Petricek    14 年前

    代码对我来说很好。

    我不确定这是你的意图还是巧合,但是你正在实现类似于“解析器组合器”的东西,这是许多学术论文的主题。我认为 Monadic Parser Combinators 可读性很强(它在haskell中有示例,但您应该能够将其翻译为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 { .. } 语法。

        2
  •  3
  •   J D    14 年前

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

    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"
    

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