Here is one way to do it if you just want to exercise the idea of pattern-matching on lists. (This way is not very efficient and is not tail-recursive, but shows one kind of pattern for looking for 3-element subsets in a list.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#light

open System.Web

let encodedUrl = "http%3A%2F%2Fcs.hubfs.net%2Fforums%2FAddPost.aspx%3FForumID%3D12"

let MyDecodeWithPatternMatching(s : string) =
    let rec Translate l =
        match l with
        | [] | [_] | [_;_] -> l
        | a :: ((b :: c :: rest) as t) when a = '%' ->
            match b,c with
            | '3','A' -> ':' :: Translate rest
            | '2','F' -> '/' :: Translate rest
            | '3','F' -> '?' :: Translate rest
            | '3','D' -> '=' :: Translate rest
            | _ -> a :: Translate t
        | h :: t -> h :: Translate t
    let newChars = Seq.to_list s |> Translate
    new string(newChars |> Array.of_list)

printfn "%s" (HttpUtility.UrlDecode encodedUrl)
printfn "%s" (MyDecodeWithPatternMatching encodedUrl)
    
By on 11/13/2008 8:43 AM ()

Thanks both.

I really didn't know about HttpUtility and it seems powerful... Anyways, I really wanted to do this as a practice exercise to start getting used to program with F#.
I am afraid though, I have to advance some little more before fully understanding brianmcn's code. Thought it would be easier; maybe I just need to some more time (just a couple of days with F#).

Thanks for everything!

By on 11/15/2008 5:08 AM ()

My prior post presupposed a pretty deep understanding of pattern-matching, so here is a commented version that explains some subtleties. (This still presupposes that you understand F# lists and constructing or matching them with "::"; if not, start with an easier problem, such as implementing "List.map", a function that applies a function to each element of a list.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
let MyDecodeWithPatternMatching(s : string) =
    let rec Translate l =
        match l with
        // if the character list has zero [], one [_], or two [_;_] elements, 
        // then there cannot be any translations left to apply, so just
        // return the the list as-is
        | [] | [_] | [_;_] -> l
        // there are at least three elements, so name the first three elements
        // "a", "b", and "c", and the remainder "rest", and also give the name
        // "t" to all-but-the-first-element.  So, for example, if the character 
        // list were ['1';'2';'3';'4';'5'] then the pattern-match below would 
        // bind:
        //     a = '1'
        //     b = '2'
        //     c = '3'
        //     rest = ['4';'5']
        //     t = ['2';'3';'4';'5']
        | a :: ((b :: c :: rest) as t) 
        // only apply this rule when the first char is '%'
              when a = '%' ->
            // if the next two chars (b and c) are certain values, then we have
            // a replacement. The whole result will be the replacement character  
            // followed by the result of making a recursive call on the rest of
            // the list
            match b,c with
            | '3','A' -> ':' :: Translate rest
            | '2','F' -> '/' :: Translate rest
            | '3','F' -> '?' :: Translate rest
            | '3','D' -> '=' :: Translate rest
            // if the next two characters after the '%' are not certain values
            // we know we want to replace, then we will just preserve the '%'
            // as-is as the first character in the result, and recurse with the
            // remainder of the list
            | _ -> a :: Translate t
        // similarly, if the first character is not '%', then just preserve that
        // character as-is in the result, and recurse with the rest.
        | h :: t -> h :: Translate t
    // string is IEnumerable<char>, so use to_list to convert it to list<char>,
    // and then apply Translate to that character list
    let newChars = Seq.to_list s |> Translate
    // (string has a constructor that takes an array, so convert the newly 
    //  converted list of characters into an array, so that we can then make
    //  a string out of it)
    new string(newChars |> Array.of_list)
By on 11/15/2008 11:55 AM ()

Oh man... thank you so much for taking the time in explaining the code. Now all seems to have full sense :). It has helped me to fix some concepts and see how far can I go combining them. Pretty enlightening indeed... I don't know what I can say...

Thanks, thanks, thanks.

By on 11/16/2008 1:54 PM ()

Also new to F# here, but I decided to have a stab at this. This appears to work:

let url = "http%3A%2F%2Fcs.hubfs.net%2Fforums%2FAddPost.aspx%3FForumID%3D12"
let transform (x:string) (y:string) (thread:string) = thread.Replace(x,y)
List.fold_right2 transform [@"%3A"; @"%2F"; @"%3F"; @"%3D"] [@":"; @"/"; @"?"; @"="] url

fold_right and fold_right2 basically work by iterating over one or two lists (depending on the function), and pass elements to some function along with a "state" variable that you are free to modify in each iteration. So basically the above code is similar to doing:

url.Replace("%3A", ":").Replace("%2F", "/").Replace("%3F", "?").Replace("%3D", "=")

Perhaps not the most efficient method (a StringBuilder intermediate representation might be the fastest), but it's a good application of functional programming.

If you want an exercise, try defining a list as follows:

let transformation_table = [("%3A", ":"), ("%2F", "/"), ("%3F", "?"), ("%3D", "=")]

and making the converting that into the necessary format for fold_right2 (hint, F# has the functionality built-in). and as a further exercise, change the internal representation of transform to operate on a StringBuilder instead of a String.

By on 11/16/2008 3:30 PM ()

Unless you really want to use your own method, you can use the following :

1
2
3
4
5
6
7
8
 

open System.Web

let url = "http%3A%2F%2Fcs.hubfs.net%2Fforums%2FAddPost.aspx%3FForumID%3D12"

HttpUtility.UrlDecode url
By on 11/13/2008 6:49 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper