因此,我正在编写解析和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.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
打破“,”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
答案 0 :(得分:3)
这是我的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()