views:

231

answers:

5

Hi, a little rusty from my Scheme days, I'd like to take 2 lists: one of numbers and one of strings, and fold them together into a single string where each pair is written like "{(ushort)5, "bla bla bla"},\n". I have most of it, i'm just not sure how to write the Fold properly:

let splitter = [|","|]
let indexes = 
  indexStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList 
let values = 
  valueStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList

let pairs = List.zip indexes values
printfn "%A" pairs

let result = pairs |> Seq.fold
    (fun acc a -> String.Format("{0}, \{(ushort){1}, \"{2}\"\}\n", 
                                acc, (List.nth a 0), (List.nth a 1)))
+1  A: 

I think you want List.fold2. For some reason the List module has a fold2 member but Seq doesn't. Then you can dispense with the zip entirely.

The types of your named variables and the type of the result you hope for are all implicit, so it's difficult to help, but if you are trying to accumulate a list of strings you might consider something along the lines of

let result = pairs |> Seq.fold
  (fun prev (l, r) -> 
          String.Format("{0}, \{(ushort){1}, \"{2}\"\}\n", prev, l, r) 
  "" pairs

My F#/Caml is very rusty so I may have the order of arguments wrong. Also note your string formation is quadratic; in my own code I would go with something more along these lines:

let strings = 
   List.fold2 (fun ss l r -> 
                 String.format ("\{(ushort){0}, \"{1}\"\}\n", l, r) :: ss)
              [] indexes values

let result = String.concat ", " strings

This won't cost you quadratic time and it's a little easier to follow. I've checked MSDN and believe I have the correct order of arguments on fold2.

Keep in mind I know Caml not F# and so I may have details or order of arguments wrong.

Norman Ramsey
+9  A: 

Your missing two things. The initial state of the fold which is an empty string and you can't use list comprehension on tuples in F#.

let splitter = [|","|]
let indexes = 
    indexStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList 
let values = 
    valueStr.Split(splitter, System.StringSplitOptions.None) |> Seq.toList

let pairs = List.zip indexes values
printfn "%A" pairs

let result = 
    pairs 
    |> Seq.fold (fun acc (index, value) -> 
        String.Format("{0}{{(ushort){1}, \"{2}\"}},\n", acc, index, value)) ""

fold2 version

let result = 
    List.fold2 
        (fun acc index value -> 
            String.Format("{0}{{(ushort){1}, \"{2}\"}},\n", acc, index, value))
        "" 
        indexes 
        values

If you are concerned with speed you may want to use string builder since it doesn't create a new string every time you append.

let result = 
    List.fold2 
        (fun (sb:StringBuilder) index value -> 
            sb.AppendFormat("{{(ushort){0}, \"{1}\"}},\n", index, value)) 
        (StringBuilder()) 
        indexes 
        values
    |> string
gradbot
of course it should be {{ and }} instead of \} and \{ in the format, i got that wrong :)
evilfred
nice catch, I updated the post for correctness sake.
gradbot
+7  A: 

Fold probably isn't the best method for this task. Its a lot easier to map and concat like this:

let l1 = "a,b,c,d,e".Split([|','|])
let l2 = "1,2,3,4,5".Split([|','|])
let pairs =
    Seq.zip l1 l2
    |> Seq.map (fun (x, y) -> sprintf "(ushort)%s, \"%s\"" x y)
    |> String.concat "\n"
Juliet
A: 

Perhaps this:

let strBuilder = new StringBuilder()
for (i,v) in Seq.zip indexes values do
     strBuilder.Append(String.Format("{{(ushort){0}, \"{1}\"}},\n", i,v))
     |> ignore

with F# sometimes is better go imperative...

primodemus
A: 

map2 or fold2 is the right way to go. Here's my take, using the (||>) operator:

let l1 = [| "a"; "b"; "c"; "d"; "e" |]
let l2 = [| "1"; "2"; "3"; "4"; "5" |]
let pairs = (l1, l2) ||> Seq.map2 (sprintf ("(ushort)%s, \"%s\""))
                      |> String.concat "\n"
Nathan Howell