Uziel
Uziel

Reputation: 349

Apply list elements to a function

Would someone please explain why the code below,

    let list = ["A"; "B"; "C"]
    let rec processList2 aList str = 
        match aList with
        | h::t  ->  let z =  str + ", " + h
                    printfn "%s" z
                    processList2 t z
        | []    ->  aList    |> ignore 

returns the following,

val list : string list = ["A"; "B"; "C"]

> processList2 list "X";;
X, A
X, A, B
X, A, B, C
val it : unit = ()
> 

instead of this?

val list : string list = ["A"; "B"; "C"]

> processList2 list "X";;
X, A
X, A, X, B
X, A, X, B, X, C
val it : unit = ()
> 

The function is recursive and passes 'z' to 'str' with each pass, so it seems like it should work...

I really appreciate the experts' help here. I am trying to learn F# and struggling with lists.

Also, how does one declare a 'list of strings?' I had an issue where a list expected to return a unit instead of a string.

Upvotes: 1

Views: 107

Answers (1)

Gary.S
Gary.S

Reputation: 7121

If we follow along with each step it should help us understand why you get the result you get:

processList2 list "X";;

The first iteration takes h::t or "A"::["B"; "C"]. It then sets z to "X" + ", " + "A".

The next iteration takes "B"::["C"]. It then sets z to "X, A" + ", " + "B".

As you can see "X" does not get inserted in each iteration. Rather z gets appended to and set through each iteration building on the last. To append "X" at each iteration it would need to be something like:

let list = ["A"; "B"; "C"]
// Append "X, " to each item
let mapList item = "X, " + item
// reduce to single comma seperated list
let redList l r = l + ", " + r    
// apply map and reduce functions to given list
let result = list |> List.map(mapList) |> List.reduce(redList)
printfn "%s" result

If you wanted you could even use String.Join to reduce the list however that requires a few more hoops to jump through:

let list = ["A"; "B"; "C"]
let mapList item = "X, " + item
let joinList (lst:list<string>) = System.String.Join(", ", lst)  
let result = list |> List.map(mapList) |> joinList   
printfn "%s" result

As for you last question: how does one declare a 'list of strings?, the answer depends on what you mean by declare. Are you trying to declare a variable of that type or a parameter that accepts it?

Declaring a variable as a certain type if generally done like this: let lst:string list = ["A"; "B"; "C"] where the type is given after the : during the declaration. If it is in a parameter than you have to be a little more explicit as you have to tell the compiler that you are setting the parameters type and not the return type:

// Set the lst parameter type to be a list<string> (same as string list)
let joinList (lst:list<string>) = System.String.Join(", ", lst)

// fails as we are telling the compiler to expect a return of string list yet we are only returning string
let joinList lst:list<string> = System.String.Join(", ", lst)

// Explicitly set both return and parameters
let joinList (lst:string list):string = System.String.Join(", ", lst)

Typically this wont be required as the type inference system in F# is very good and figuring out what type you want/need in these situations.

Upvotes: 2

Related Questions