Refactoring small F# function
I've made the following F# function that will get me an url from the html contents of a web page:
let getPicUrl (urlContents : string) =
let START_TOKEN = "jpg_url="
let startIndex = urlContents.IndexOf(START_TOKEN)
let endIndex = urlContents.IndexOf("&", startIndex)
let s = startIndex + START_TOKEN.Length
let l = endIndex-startIndex-START_TOKEN.Length
urlContents.Substring(s, l)
what the last line, urlContents.Substring(s, l), actually needs is only s and l, so I was wondering whether I could refactor parts of this function into some internal functions so I'd let my intentions be clearer. Ideally getPicUrl would only have 2 let instruc开发者_如何学Gotions, s and l, and all the others would be internal definitions to those let instructions. If this can in any way be achieved or not is another story..
The only obvious way I can think at the moment to improve the above code would be to switch endIndex of place so we'd have
let getPicUrl (urlContents : string) =
let START_TOKEN = "jpg_url="
let startIndex = urlContents.IndexOf(START_TOKEN)
let s = startIndex + START_TOKEN.Length
let l =
let endIndex = urlContents.IndexOf("&", startIndex)
endIndex-startIndex-START_TOKEN.Length
urlContents.Substring(s, l)
but I keep wondering if there'd be a clearer way of organizing this function's let definitions.
Firstly, your function is buggy. A non-matching string will make it grumpy.
I like regexes for this sort of thing. With this active pattern:
open System.Text.RegularExpressions
let (|Regex|_|) pattern input =
let m = Regex.Match(input, pattern)
if m.Success then Some(List.tail [for g in m.Groups -> g.Value])
else None
you can do:
let tryGetPicUrl = function
| Regex @"jpg_url=([^&]+)&" [url] -> Some url
| _ -> None
You could also turn your original approach into an active pattern:
let (|Between|_|) (prefix:string) (suffix:string) (value:string) =
match value.IndexOf(prefix) with
| -1 -> None
| s ->
let n = s + prefix.Length + 1
match value.IndexOf(suffix, n) with
| -1 -> None
| e -> Some (value.Substring(n, e - n))
and do:
let tryGetPicUrl = function
| Between "jpg_url" "&" url -> Some url
| _ -> None
You can write it this way:
let getPicUrl (urlContents : string) =
let s =
let START_TOKEN = "jpg_url="
let startIndex = urlContents.IndexOf(START_TOKEN)
startIndex + START_TOKEN.Length
let l =
let endIndex = urlContents.IndexOf("&", s)
endIndex-s
urlContents.Substring(s, l)
Another option would be to use split method of string (I hope the string is not too long as that would be a performance hit) and use option type to indicate whether the URL was found or not.
let getPicUrl (urlContents : string) =
let splitAndGet n (sep:string) (str:string) =
let spl = str.Split([|sep|],StringSplitOptions.None)
match spl.Length with
| x when x > n -> Some (spl.[n])
| _ -> None
match urlContents |> splitAndGet 1 "jpg_url=" with
| Some str -> str |> splitAndGet 0 "&"
| _ -> None
加载中,请稍侯......
精彩评论