2010-09-01 34 views
7

Trong một nỗ lực để hiểu khả năng lập trình chức năng, tôi đã tập hợp một số chức năng cơ bản mà bạn có thể soạn cùng nhau để xây dựng các biểu thức chính quy phức tạp. Bây giờ sau khi một số thử nghiệm tôi đã tìm thấy điều này hoạt động nhưng bạn có thể viết một số mã khủng khiếp trong bất kỳ ngôn ngữ sẽ làm việc. Đây có phải là loại mã bạn sẽ tìm thấy một lập trình viên F # chuyên nghiệp hay tôi đang lạm dụng tính năng này?Tôi có sử dụng đúng thành phần chức năng không?

Lưu ý:test cụ thể là những gì tôi đang đề cập đến.

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() 

Trả lời

8

Mã có vẻ khá tốt với tôi.

Tôi không chắc đây có phải là ý định của bạn hay trùng hợp ngẫu nhiên hay không, nhưng bạn đang triển khai một cái gì đó khá giống với "trình kết hợp phân tích cú pháp", đây là chủ đề của nhiều bài báo học thuật :-). Tôi nghĩ rằng Monadic Parser Combinators là khá dễ đọc (nó có các ví dụ trong Haskell, nhưng bạn sẽ có thể dịch chúng sang F #).

Về toán tử thành phần hàm. Tôi thường không phải là một fan hâm mộ lớn của việc sử dụng các nhà điều hành quá nhiều, bởi vì nó thường obfuscates mã. Tuy nhiên, trong ví dụ của bạn, nó có ý nghĩa tốt bởi vì bạn có thể dễ dàng tưởng tượng rằng >> có nghĩa là "nhóm này nên được theo sau bởi nhóm đó", dễ hiểu.

Sự thay đổi nhỏ duy nhất mà tôi có thể làm là chọn một số nhà điều hành tùy chỉnh tốt đẹp cho các hoạt động disjunction và xác định một vài hoạt động nguyên thủy hơn, do đó bạn có thể viết cho ví dụ này:

// 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 

Đây là chi tiết mô tả cấp cao hơn, do đó, nó loại bỏ một số toán tử >> có lợi cho các hàm có tính mô tả hơn (và đóng gói >>). Tôi cũng đã thay đổi quantify để thực hiện nhiều đối số thay vì một tripple (đó là một thay đổi nhỏ)

Nếu bạn muốn chơi thêm nữa, thì bạn có thể xem bài viết và cố gắng viết trình tạo biểu thức tính toán F # sẽ cho phép bạn sử dụng cú pháp parser { .. }.

+1

Thật tuyệt khi biết rằng tôi đang tiến bộ trong các kỹ năng lập trình chức năng của mình. Bạn đã cho tôi thực sự vui mừng để thử và bọc với tất cả vào cú pháp biểu thức tính toán thanh lịch. :) Dù sao cảm ơn lời khuyên của bạn và giấy * (Tôi là một fan hâm mộ của bất cứ điều gì Erik Meijer.) *. – ChaosPandion

+0

Giấy thú vị; Cảm ơn bạn đã đăng các liên kết. – TechNeilogy

3

Đây thường là phong cách tốt nhưng bạn thiếu một số thủ thuật và vẫn còn khá nhiều dự phòng. Có thể giống như thế này:

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" 

Bạn có thể muốn tích lũy giá trị đại diện cho tính toán hơn là đóng vì nó giúp gỡ lỗi dễ dàng hơn nhiều.

+0

Wow Tôi cảm thấy ngớ ngẩn vì không đến với chức năng 'hợp lệ 'đó. Cảm ơn lời đề nghị của bạn. – ChaosPandion

Các vấn đề liên quan