Может ли кто-нибудь помочь мне сравнить использование F # поверх C # в этом конкретном примере (выражения IP-адреса)? - PullRequest
0 голосов
/ 12 мая 2010

Итак, я пишу код для синтаксического анализа и выражения IP-адреса и превращаю его в регулярное выражение, которое можно использовать для строки IP-адреса и возвращать логический ответ. Я написал код на C # (OO), и это было 110 строк кода. Я пытаюсь сравнить объем кода и выразительность C # с F # (я программист C # и нуб в F #). Я не хочу публиковать C # и F # только потому, что не хочу загромождать пост. При необходимости я сделаю это.

Во всяком случае, я приведу пример. Вот выражение:

192.168.0.250,244-248,108,51,7; 127.0.0.1

Я хотел бы взять это и превратить в следующее регулярное выражение:

((192 \ .168 \ .0 \ (250 |. 244 | 245 | 246 | 247 | 248 | 108 | 51 | 7)) | (127 \ 0,0 \ 0,0 \ 0,1))

Вот несколько шагов, которым я следую:

Операции:

Пробел на ";" 192.168.0.250,244-248,108,51,7 127.0.0.1

Пробел на "." 192 168 0 250 244-248,108,51,7

Break by "," 250 244-248 108 51 7 Перерыв на "-" 244 248

Я придумал F #, который производит вывод. Я пытаюсь перенаправить все операции, перечисленные выше, так как думаю, что это будет более выразительным. Кто-нибудь может сделать этот код лучше? Научи меня чему-нибудь:)

open System

let createItemArray (group:bool) (y:char) (items:string[]) = 
  [|
    let indexes = items.Length - 1
    let group = indexes > 0 && group
    if group then
      yield "("
    for i in 0 .. indexes do
      yield items.[i].ToString()
      if i < indexes then
        yield y.ToString()
    if group then
      yield ")"
  |] 

let breakBy (group:bool) (x:string) (y:char): string[] = 
  x.Split(y)
    |> createItemArray group y 

let breakItem  (x:string) (y:char): string[] = breakBy false x y
let breakGroup  (x:string) (y:char): string[] = breakBy true x y

let AddressExpression address:string = 
    let builder = new System.Text.StringBuilder "("
    breakGroup address ';'
    |> Array.collect (fun octet -> breakItem octet '.')
    |> Array.collect (fun options -> breakGroup options ',')
    |> Array.collect (fun (ranges : string) -> 
                            match (breakGroup ranges '-') with
                            | x when x.Length > 3
                              -> match (Int32.TryParse(x.[1]), Int32.TryParse(x.[3]))    with
                                  | ((true, a) ,(true, b))
                                      -> [|a .. b|]
                                          |> Array.map (int >> string)
                                          |> createItemArray false '-'
                                  | _ -> [|ranges|]
                            | _ -> [|ranges|]
                    )
    |> Array.iter (fun item ->
                    match item with
                    | ";" -> builder.Append ")|("
                    | "." -> builder.Append "\."
                    | "," | "-" -> builder.Append "|"
                    | _ -> builder.Append item
                    |> ignore
                  )
    builder.Append(")").ToString()

let address = "192.168.0.250,244-248,108,51,7;127.0.0.1"
AddressExpression address

1 Ответ

3 голосов
/ 12 мая 2010

Вот мой в 63 строках F # (включая один тестовый пример); это сработало в первый раз, и мне кажется, что оно читаемо. Это типичный парсер, за которым следует симпатичный принтер. Что мы думаем?

type IPs = IP[]
and IP = IP of OrParts * OrParts * OrParts * OrParts
and OrParts = Or of Part[]
and Part = Num of int | Range of int * int

let Valid(x) = if x < 0 || x > 255 then failwithf "Invalid number %d" x

let rec parseIPs (s:string) =
    s.Split [|';'|] |> Array.map parseIP
and parseIP s =
    let [|a;b;c;d|] = s.Split [|'.'|]
    IP(parseOrParts a, parseOrParts b, parseOrParts c, parseOrParts d)
and parseOrParts s =
    Or(s.Split [|','|] |> Array.map parsePart)
and parsePart s =
    if s.Contains("-") then
        let [|a;b|] = s.Split [|'-'|]
        let x,y = int a, int b
        Valid(x)
        Valid(y)
        if x > y then failwithf "Invalid range %d-%d" x y
        Range(x, y)
    else
        let x = int s
        Valid(x)
        Num(x)

let rec printIPsAsRegex ips =
    let sb = new System.Text.StringBuilder()
    let add s = sb.Append(s:string) |> ignore
    add "("
    add(System.String.Join("|", ips |> Array.map printIPAsRegex))
    add ")"
    sb.ToString()
and printIPAsRegex (IP(a, b, c, d)) : string =
    let sb = new System.Text.StringBuilder()
    let add s = sb.Append(s:string) |> ignore
    add "("
    printPartsAsRegex add a
    add "."
    printPartsAsRegex add b
    add "." 
    printPartsAsRegex add c
    add "."
    printPartsAsRegex add d
    add ")"
    sb.ToString()
and printPartsAsRegex add (Or(parts)) =
    match parts with
    | [| Num x |] -> // exactly one Num
        add(string x)
    | _ ->
        add "("
        add(System.String.Join("|", parts |> Array.collect (function
                | Num x -> [| x |]
                | Range(x,y) -> [| x..y |])
            |> Array.map (fun x -> x.ToString())))
        add ")"

let Main() =
    let ips = parseIPs "192.168.0.250,244-248,108,51,7;127.0.0.1"
    printfn "%s" (printIPsAsRegex ips)
Main()                
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...