I am trying to achieve the following. Input is list [8;9;4;5;7] and output should be
"8,9,4,5,7," Note the "," in the output
I tried the following
let rec ConvertToString list =
match list with
| head :: tail -> head.ToString() + ConvertToString tail
| [] -> ""
let op= [8;9;4;5;7] |> ConvertToString
But the output which i get is val me : string = "89457"
Can anyone kindly suggest how to get get the "," in the output. The function should be generic.
You need to add the comma between the head and the converted tail, and need another case to convert the last element so you don't add a separating comma.
let rec ConvertToString list =
match list with
| [l] -> l.ToString()
| head :: tail -> head.ToString() + "," + ConvertToString tail
| [] -> ""
Note you can also define your function using String.concat:
let ConvertToString l = l |> List.map (fun i -> i.ToString()) |> String.concat ","
or String.Join:
let ConvertToString (l: 'a seq) = System.String.Join(",", l)
If you only want to allow ConvertToString to take int list arguments, you can specify the type of the input argument explicitly:
let ConvertToString (l : int list) = ...
I think this is a nice version for integer lists:
let convertToString = List.map (sprintf "%i") >> String.concat ","
or if you want a generic version:
let convertToString l = l |> List.map (sprintf "%A") |> String.concat ","
Just another way to do this:
let ConvertToString (l: int list) =
match l with
| [] -> ""
| h :: t -> List.fold (fun acc x -> acc + "," + x.ToString()) (h.ToString()) t
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) |> (+) <| "]"
It just returns some " ", but I need to return "none".
How can I do this ?
let cat(filenames: string list) : string option =
try
let b = filenames |> List.map (fun x -> (defaultArg (concat(x)) ""))
Some (String.concat "" b)
with _ -> None
It returns some "" because an empty list doesn't cause an exception. You need to match on an empty list and return None. I also am not sure the List.map aligns with the concat you're trying to do here, perhaps you meant List.reduce ?
Something like this might work.
let cat filenames =
match filenames with
| [] -> None
| l -> l |> List.reduce (+) |> Some
I need to extract the sequence of equal chars in a text.
For example:
The string "aaaBbbcccccccDaBBBzcc11211" should be converted to a list of strings like
["aaa";"B";"bb";"ccccccc";"D";"a";"BBB";"z";"cc";"11";"2";"11"].
That's my solution until now:
let groupSequences (text:string) =
let toString chars =
System.String(chars |> Array.ofList)
let rec groupSequencesRecursive acc chars = seq {
match (acc, chars) with
| [], c :: rest ->
yield! groupSequencesRecursive [c] rest
| _, c :: rest when acc.[0] <> c ->
yield (toString acc)
yield! groupSequencesRecursive [c] rest
| _, c :: rest when acc.[0] = c ->
yield! groupSequencesRecursive (c :: acc) rest
| _, [] ->
yield (toString acc)
| _ ->
yield ""
}
text
|> List.ofSeq
|> groupSequencesRecursive []
groupSequences "aaaBbbcccccccDaBBBzcc11211"
|> Seq.iter (fun x -> printfn "%s" x)
|> ignore
I'm a F# newbie.
This solution can be better?
Here a completely generic implementation:
let group xs =
let folder x = function
| [] -> [[x]]
| (h::t)::ta when h = x -> (x::h::t)::ta
| acc -> [x]::acc
Seq.foldBack folder xs []
This function has the type seq<'a> -> 'a list list when 'a : equality, so works not only on strings, but on any (finite) sequence of elements, as long as the element type supports equality comparison.
Used with the input string in the OP, the return value isn't quite in the expected shape:
> group "aaaBbbcccccccDaBBBzcc11211";;
val it : char list list =
[['a'; 'a'; 'a']; ['B']; ['b'; 'b']; ['c'; 'c'; 'c'; 'c'; 'c'; 'c'; 'c'];
['D']; ['a']; ['B'; 'B'; 'B']; ['z']; ['c'; 'c']; ['1'; '1']; ['2'];
['1'; '1']]
Instead of a string list, the return value is a char list list. You can easily convert it to a list of strings using a map:
> group "aaaBbbcccccccDaBBBzcc11211" |> List.map (List.toArray >> System.String);;
val it : System.String list =
["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
This takes advantage of the String constructor overload that takes a char[] as input.
As initially stated, this implementation is generic, so can also be used with other types of lists; e.g. integers:
> group [1;1;2;2;2;3;4;4;3;3;3;0];;
val it : int list list = [[1; 1]; [2; 2; 2]; [3]; [4; 4]; [3; 3; 3]; [0]]
How about with groupby
"aaaBbbcccccccD"
|> Seq.groupBy id
|> Seq.map (snd >> Seq.toArray)
|> Seq.map (fun t -> new string (t))
If you input order matters, here is a method that works
"aaaBbbcccccccDaBBBzcc11211"
|> Seq.pairwise
|> Seq.toArray
|> Array.rev
|> Array.fold (fun (accum::tail) (ca,cb) -> if ca=cb then System.String.Concat(accum,string ca)::tail else string(ca)::accum::tail) (""::[])
This one is also based on recursion though the matching gets away with smaller number of checks.
let chop (txt:string) =
let rec chopInner txtArr (word: char[]) (res: List<string>) =
match txtArr with
| h::t when word.[0] = h -> chopInner t (Array.append word [|h|]) res
| h::t when word.[0] <> h ->
let newWord = word |> (fun s -> System.String s)
chopInner t [|h|] (List.append res [newWord])
| [] ->
let newWord = word |> (fun s -> System.String s)
(List.append res [newWord])
let lst = txt.ToCharArray() |> Array.toList
chopInner lst.Tail [|lst.Head|] []
And the result is as expected:
val text : string = "aaaBbbcccccccDaBBBzcc11211"
> chop text;;
val it : string list =
["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
When you're folding, you'll need to carry along both the previous value and the accumulator holding the temporary results. The previous value is wrapped as option to account for the first iteration. Afterwards, the final result is extracted and reversed.
"aaaBbbcccccccDaBBBzcc11211"
|> Seq.map string
|> Seq.fold (fun state ca ->
Some ca,
match state with
| Some cb, x::xs when ca = cb -> x + ca::xs
| _, xss -> ca::xss )
(None, [])
|> snd
|> List.rev
// val it : string list =
// ["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
Just interesting why everyone publishing solutions based on match-with? Why not go plain recursion?
let rec groups i (s:string) =
let rec next j = if j = s.Length || s.[i] <> s.[j] then j else next(j+1)
if i = s.Length then []
else let j = next i in s.Substring(i, j - i) :: (groups j s)
"aaaBbbcccccccDaBBBzcc11211" |> groups 0
val it : string list = ["aaa"; "B"; "bb"; "ccccccc"; "D"; "a"; "BBB"; "z"; "cc"; "11"; "2"; "11"]
As someone other here:
Know thy fold ;-)
let someString = "aaaBbbcccccccDaBBBzcc11211"
let addLists state elem =
let (p, ls) = state
elem,
match p = elem, ls with
| _, [] -> [ elem.ToString() ]
| true, h :: t -> (elem.ToString() + h) :: t
| false, h :: t -> elem.ToString() :: ls
someString
|> Seq.fold addLists ((char)0, [])
|> snd
|> List.rev
I've got this function, what I want to do is take a list, split every element in the list on '.' and place the separated elements in 2 different lists, my problem now is I'm getting an error that says the let expression is unfinished and I think it has to do with not having a return value, is there a way to bypass this or am I doing something completely wrong?
let klist = []
let olist = []
let listSplit list =
match list.Split '.' with
| [| x;y |] -> x :: klist, y :: olist
| [| x |] -> x :: klist
| _ -> None;;
It looks like you want something like
let listSplit (list: string list) =
let acc (kl, ol) = function
| [| x; y |] -> (x::kl, y::ol)
| [| x |] -> (x::kl, ol)
list |> List.map (fun s -> s.Split('.'))
|> List.filter(fun a -> a.Length = 1 || a.Length = 2)
|> List.fold acc ([],[])
Your existing code has a few problems:
list has no split method. Strings do have a Split method, so you probably want to split each string in the input list, which you can do with List.map.
Your match expression does not type check, and each branch has a different type - the first returns string list * string list, the second string list and the third a' option.
I'm positive that there is a better way to swap items in a list by pairs ( [1;2;3;4] -> [2;1;4;3] ) as I'm doing too many appends for my liking but I'm not sure how best to do it.
let swapItems lst =
let f acc item =
match acc with
| [] -> [item]
| hd :: next :: tl when tl <> [] -> [next] # tl # [item;hd]
| _ -> item :: acc
List.fold f [] lst
How can I improve this? This only works on lists that have an even length.
Simplest possible solution:
let rec swapItems = function
| a::b::xs -> b::a::swapItems xs
| xs -> xs
I like to make the names of variables that are sequences like lists "plural", e.g. xs instead of x.
Note that this is not tail recursive so it will stack overflow if you give it a very long list.
What about this:
let rec swapItems = function
| []
| _::[] as l -> l
| a::b::t ->
b::a::(swapItems t)
?
Using higher order functions this can be done as:
let swapItems l =
l |> List.toSeq |> Seq.pairwise
|> Seq.mapi (fun i (a,b) -> if i % 2 = 0 then seq [b;a] else Seq.empty)
|> Seq.concat |> Seq.toList