I can’t find a simple working example of the use of Map.fold anywhere. I have seen the F# for fun and profit and the MSFT documents on ‘fold’.
Can you provide a short working example of the use of Map.fold?
let capitals =
[("Australia", "Canberra"); ("Canada", "Ottawa"); ("China", "Beijing");
("Denmark", "Copenhagen"); ("Egypt", "Cairo"); ("Finland", "Helsinki");
("France", "Paris"); ("Germany", "Berlin"); ("India", "New Delhi");
("Japan", "Tokyo"); ("Mexico", "Mexico City"); ("Russia", "Moscow");
("Slovenia", "Ljubljana"); ("Spain", "Madrid"); ("Sweden", "Stockholm");
("Taiwan", "Taipei"); ("USA", "Washington D.C.")]
|> Map.ofList
printfn "%A\n" capitals
printfn "%s"
(Map.fold
(fun acc key value -> (acc + "(" + key + "," + value + ")"))
""
capitals)
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
If I have a list:
let list123 = [ 1; 2; 3 ]
what would be the most F#-approach to converting it into a string "[ 1; 2; 3 ]"? All questions I have found regarding list to string conversion, revolves around only taking the list elements, but I want the entire list expression.
As F# is a .NET language I don't see why System.String.Join is less F#-ish than anything else:
module List =
let toString (lst: 'a list) =
match lst with
| [] -> "[]"
| _ -> sprintf "[ %s ]" (String.Join("; ", lst))
But if you insist doing it by your self in "real" F# it could be something like:
module List =
let join lst =
match lst with
| [] -> ""
| [ h ] -> h |> string
| h::t ->
sprintf "%s" (t |> List.fold (fun res a -> sprintf "%s; %A" res a) (h |> string))
let formatAsString prefix postfix lst =
match lst |> join with
| "" -> sprintf "%s%s" prefix postfix
| str -> sprintf "%s %s %s" prefix str postfix
let toString lst = formatAsString "[" "]" lst
let empty = []
let one = [ 1 ]
let four = [ 1; 2; 3; 4 ]
printfn "%s" (empty |> List.toString)
printfn "%s" (one |> List.toString)
printfn "%s" (four |> List.toString)
outputs:
[]
[ 1 ]
[ 1; 2; 3; 4 ]
Not quite sure what you're asking, but here goes:
let s = list123 |> Seq.map string |> String.concat "; " |> sprintf "[ %s ]"
EDIT
It has the drawback that an empty list becomes [ ] rather than [], and this can be fixed e.g. by using a match similar to what FRocha does in his answer.
Use the string function:
string list123 //val it : string = "[1; 2; 3]"
EDIT
Addressing the good point made by Bent Tranberg.
let inline listToString l =
match l with
| [] -> "[]"
| l ->
(l |> List.head |> string |> (+) "[",List.tail l)
||> List.fold (fun acc elem -> acc + "; " + string elem) |> (+) <| "]"
This is a json generation :
let strGen = Arb.Default.String()
|> Arb.toGen
strGen
|> Gen.arrayOf
|> Gen.map (String.concat "\", \"")
|> Gen.map (fun strs -> "[\"" + strs + "\"]")
How can I have the string that the json have been created from in my test body to assert the final result.
My original answer was to use Gen.map2 to combine two generators, one for the string array and one for the json string. But Gen.map2 is specifically designed to let two independent generators be combined, i.e., the result of one generator won't affect the result of the other one. (E.g., rolling two dice: the result of the first die is independent of the result of the second die). What you need is a simple Gen.map that takes the string array generator and produces a tuple of (string array, json). Like so:
let strGen = Arb.Default.String() |> Arb.toGen
let arrayGen = strGen |> Gen.arrayOf
arrayGen |> Gen.map (fun array ->
let json =
array
|> String.concat "\", \""
|> fun strs -> "[\"" + strs + "\"]")
array,json)
Unlike my answer below which combined two independent generators, here there is only ONE generator, whose value is used to produce both the array and the json values. So these values will be dependent rather than independent, and the json will always match the string array.
Original, INCORRECT, answer below, preserved in case the contrast between the two answers is useful:
Easy. Just save the array generator, and re-use it later, using Gen.map2 to combine the array and the json. E.g.:
let strGen = Arb.Default.String()
|> Arb.toGen
let arrayGen = strGen |> Gen.arrayOf
let jsonGen =
arrayGen
|> Gen.map (String.concat "\", \"")
|> Gen.map (fun strs -> "[\"" + strs + "\"]")
Gen.map2 (fun array json -> array,json) arrayGen jsonGen
And now you have a generator that produces a 2-tuple. The first element of the tuple is the string array, and the second element is the json that was generated.
BTW, your JSON-creating code isn't quite correct yet, because if the generated string contains quotation marks, you'll need to quote them in some way or your generated JSON will be invalid. But I'll let you handle that, or ask a new question about that if you don't know how to handle that. The "single responsibility principle" applies to Stack Overflow questions, too: each question should ideally be about just one subject.
Can't seem to be able to put the code in comments, so here's a cleaned up version:
let isDigitOrWord i =
i |> String.isNullOrEmpty
|> not && Regex.IsMatch(i,"^[a-zA-Z0-9 ]*$")
let strGen = Arb.Default.String() |> Arb.toGen
Gen.arrayOf strGen
|> Gen.map (fun array ->
let array = array |> Array.filter isDigitOrWord
let json =
array
|> String.concat "\", \""
|> fun strs -> if strs|> String.isEmpty then strs else "\"" + strs + "\""
|> fun strs -> "[" + strs + "]"
array,json)
Can someone tell me why this works
let createRandomList = List.init 9 (fun _ -> randomNumberGenerator.Next(0,9))
let concatRandomList =
createRandomList
|> Seq.map string
|> String.concat ""
and this does not?
let createConcatRandomList = List.init 9 (fun _ -> randomNumberGenerator.Next(0,9))
|> Seq.map string
|> String.concat ""
I am getting an "Incomplete value or function definition" on the second code block
Thanks in advance
In the second example you need to indent the |> to the same level as List
When I use Console.WriteLine to print a list, it defaults to only showing the first three elements. How do I get it to print the entire contents of the list?
You can use the %A format specifier along with printf to get a 'beautified' list printout, but like Console.WriteLine (which calls .ToString()) on the object, it will not necessarily show all the elements. To get them all, iterate over the whole list. The code below shows a few different alternatives.
let smallList = [1; 2; 3; 4]
printfn "%A" smallList // often useful
let bigList = [1..200]
printfn "%A" bigList // pretty, but not all
printfn "Another way"
for x in bigList do
printf "%d " x
printfn ""
printfn "Yet another way"
bigList |> List.iter (printf "%d ")
printfn ""
You can iterate over the it, using the List.iter function, and print each element:
let list = [1;2;3;4]
list |> List.iter (fun x -> printf "%d " x)
More info:
Lists in F# (MSDN)
Here's simple alternative that uses String.Join:
open System
let xs = [1; 2; 3; 4]
let s = "[" + String.Join("; ", xs) + "]"
printfn "%A" s
I'm trying this at the moment, but I haven't quite got the method signature worked out... anyone? messages is a field of seq[string]
let messageString = List.reduce(messages, fun (m1, m2) -> m1 + m2 + Environment.NewLine)
> String.concat " " ["Juliet"; "is"; "awesome!"];;
val it : string = "Juliet is awesome!"
Not exactly what you're looking for, but
let strings = [| "one"; "two"; "three" |]
let r = System.String.Concat(strings)
printfn "%s" r
You can do
let strings = [ "one"; "two"; "three" ]
let r = strings |> List.fold (+) ""
printfn "%s" r
or
let strings = [ "one"; "two"; "three" ]
let r = strings |> List.fold (fun r s -> r + s + "\n") ""
printfn "%s" r
I'd use String.concat unless you need to do fancier formatting and then I'd use StringBuilder.
(StringBuilder(), [ "one"; "two"; "three" ])
||> Seq.fold (fun sb str -> sb.AppendFormat("{0}\n", str))
just one more comment,
when you are doing with string, you'd better use standard string functions.
The following code is for EulerProject problem 40.
let problem40 =
let str = {1..1000000} |> Seq.map string |> String.concat ""
let l = [str.[0];str.[9];str.[99];str.[999];str.[9999];str.[99999];str.[999999];]
l |> List.map (fun x-> (int x) - (int '0')) |> List.fold (*) 1
if the second line of above program uses fold instead of concat, it would be extremely slow because each iteration of fold creates a new long string.
System.String.Join(Environment.NewLine, List.to_array messages)
or using your fold (note that it's much more inefficient)
List.reduce (fun a b -> a ^ Environment.NewLine ^ b) messages