I have the string "H1E1T1H1" for which I want to replace each '1' with a string such as "OP" and I think that this would be easiest accomplished using lists because of the ease of adding elements. However, I wonder how I can initialize a list from a sting? (if using lists actually does not make it much easier, please correct me :) )
The easiest way (and it is idiomatic in F#) is to use String.Replace method as follow:
let str = "H1E1T1H1"
let result = str.Replace("1","OP")
But in case you want FP just because you can... :)
"H1E1T1H1"
|> Seq.map (function | '1' -> "OP" | x -> string x)
|> String.concat ""
In case you wanted to replace the same character with different strings according to the character's index
"H1E1T1H1"
|> Seq.mapi (fun i x ->
match i,x with
| (i, '1') when i < 4 || i > 6 -> "OP"
| (_, x) -> string x)
|> String.concat ""
Related
Say you have a list of strings: [ "a"; "b"; "c" ] and you want to transform it to a single string like so: "a,b,c" notice that the last comma is missing.
I find this case comes up again and again for me, think of all the programming languages that do not allow the trailing comma to be there, and you are building some kind of code generator.
I usually end up with something like:
let listOfThings = ["a";"b";"c"]
let folded =
listOfThings
|> List.map (fun i -> i + ",")
|> List.fold (+) ""
|> (fun s -> s.Substring(0, s.Length - 1))
I feel like there is some fold like function already because this seems such a basic use case, and I just can't figure out what would be it's name, or by what name to search for it.
A fold applies your folding function recursively over all values of the list, starting with an initial state, which you don't particularly want in this case.
It's simpler to use a reduce which uses the list's head as its starting state:
listOfThings |> List.reduce (fun sum cur -> sum + "," + cur) // "a,b,c"
A minor drawback is that since it uses the list head, calling reduce with an empty list would fail. You can mitigate that with a check for an empty list.
Without any built-ins, as you have described, we skip the addition of the trailing comma for the last element:
let rec join = function
| [] -> ""
| [x] -> x
| x::xs -> x + "," + join xs
["a"; "b"; "c"] |> join // a,b,c
However, the most efficient method would be to use String.Join which internally uses a StringBuilder, while reduce allocates a new string for every call:
String.Join(",", listOfThings) // "a,b,c"
A reduction applied to the elements of a list is necessarily of the same type as these are. In contrast, the accumulator (also called state) of a fold can be of a different type, which is more versatile. The signatures make it apparent:
val reduce: ('a -> 'a -> 'a) -> 'a list -> 'a
val fold: ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
A possible approach might consist in provision of a different folding function for the first element of the list (or for the last, in the case of foldBack). Here it is also prudent to check for an empty list, as it is with reduce.
let fold1 folderN folder0 state = function
| [] -> state
| x::xs -> List.fold folderN (folder0 state x) xs
// val fold1 :
// folderN:('a -> 'b -> 'a) ->
// folder0:('a -> 'b -> 'a) -> state:'a -> _arg1:'b list -> 'a
Now we can fold into a list, or even use a StringBuilder:
([], ["a";"b";"c"])
||> fold1
(fun s t -> t::", "::s)
(fun s t -> t::s)
|> List.rev
// val it : string list = ["a"; ", "; "b"; ", "; "c"]
(System.Text.StringBuilder(), ["a";"b";"c"])
||> fold1
(fun s t -> s.Append(", ").Append t)
(fun s t -> s.Append t)
|> string
// val it : string = "a, b, c"
Indeed, there is a built in function that does this:
let s = [ "a"; "b"; "c" ]
String.concat ", " s // "a, b, c"
I'm trying to make a function that checks a lists sublists to see if they have equal length and returns a bool value.
[ [1;2;3]; [4;5;6] ] (return true)
[ [1;2;3]; [4;5] ] (return false)
I'm trying to learn about lambda's and list modules.
So far I have:
let isTable (lst : 'a list list) : bool =
List.forall (fun x -> x.Length = 2) ([ [1;2;3]; [4;5;6] ])
It says x.Length is wrong somehow.
Can someone explain what I am doing wrong?
The problem with your code is that the F# type inference does not know what is type of x when checking the lambda function and so it cannot check whether the object will have a member Length. The type inference checks your program from left to right and so it only figures out that x will be a list when it gets to the argument [ [1;2;3]; [4;5;6] ] later in your code.
There is a couple of ways to fix this. You can use List.length which is a function and not an instance member, so the inference can check that:
let isTable (lst : 'a list list) : bool =
List.forall (fun x -> List.length x = 2) [ [1;2;3]; [4;5;6] ]
A nicer alternative is to use the |> operator which passes the thing on the left to the function on the right, so writing x |> f is the same as calling f x. This puts the input to the left, so the inference will work:
let isTable (lst : 'a list list) : bool =
[ [1;2;3]; [4;5;6] ] |> List.forall (fun x -> x.Length x = 2)
Finally, you could also add a type annotation:
let isTable (lst : 'a list list) : bool =
List.forall (fun (x:_ list) -> x.Length = 2) [ [1;2;3]; [4;5;6] ]
Out of these three, I think the most idiomatic solution is to use |>, but List.length is also common.
Try this code:
let isTable (lst: 'a list list) =
match lst with
| [] | [[]] -> false
| []::t -> false
| [_] -> true
| h::t -> t |> List.forall(fun l -> l.Length = h.Length)
The issue you are having is that the type inference system of F# does not firmly recognize x as being a list at the point where you want to do x.Length. That may seem strange because if you use Intellisense (for instance by hovering over x) it will tell you that it is a list, yet the compiler complains.
The reason for that is that F#'s type inference does not work as well when working with Object Oriented (OO) dot . notation, it does much better when using functional dot . notation. To differentiate between the two, the convention in F# (and .Net) is that class members (methods and properties) start with a capital letter (also known as Pascal Case) hence x.Length. On the other hand, functional style code (like functions in a module and/or record members) start with a lower case (known as Camel Case) like List.length.
Notice the difference between the 2 styles:
OO, invoke a method: x.Length
Functional, call a function: List.length x
If you want to use the OO style, typically the solution is to add a type annotation, which you can do in several ways:
fun (x:_ list) -> x.Length = 2
fun x -> (x:_ list).Length = 2
In general, it is better practice to use the functional style. But, you do not always have a choice. For instance there are many String methods that do not have a functional equivalent:
fun (s:string) -> s.StartsWith "Hello"
I also would like to point out that your code as stated does not really do what you want. It returns true only if all lists are of length 2, not if all of them are the same length.
kagetoki's solution works and also demonstrates the use of pattern matching for lists.
Here is a simplified version:
let isTable lst =
match lst with
| h::t -> t |> List.forall(fun (l:_ list) -> l.Length = h.Length)
| _ -> true
Notice that by stating that l is a list, it already knows that h is also a list.
Finally, just for fun, a super compact (but obscure) version :
let isTable =
function
| h::t -> t |> List.forall (List.length >> (=) h.Length)
| _ -> true
I have a list of names, and I need to output a single string that shows the letters from the names in the order they appear without the duplicates (e.g. If the list is ["John"; "James"; "Jack"], the output string should be Johnamesck). I've got a solution (folding all the names into a string then parse), but I feel like I'm cheesing it a bit by making my string mutable.
I also want to state this is not a school assignment, just an exercise from a work colleague as I'm coming into F# from only ever knowing Java Web stuff.
Here is my working solution (for insight purposes):
let lower = ['a' .. 'z']
let upper = ['A' .. 'Z']
let mutable concatedNames = ["John"; "James"; "Jack"] |> List.fold (+) ""
let greaterThanOne (length : int) = length > 1
let stripChars (str : string) letter =
let parts = str.Split([| letter |])
match greaterThanOne (Array.length parts) with
| true -> seq {
yield Array.head parts
yield string letter
yield! Array.tail parts
}
|> String.concat ""
| _ -> str
let killAllButFirstLower = lower |> List.iter (fun letter -> concatedNames <- (stripChars concatedNames letter))
let killAllButFirstUpper = upper |> List.iter ( fun letter -> concatedNames <- (stripChars concatedNames letter))
printfn "All names with duplicate letters removed: %s" concatedNames
I originally wanted to do this explicitly with functions alone and had a solution previous to above
let lower = ['a' .. 'z']
let upper = ['A' .. 'Z']
:
:
:
let lowerStripped = [""]
let killLowerDuplicates = lower |> List.iter (fun letter ->
match lowerStripped.Length with
| 1 ->
(stripChars concatedNames letter)::lowerStripped |> ignore
| _ -> (stripChars (List.head lowerStripped) letter)::lowerStripped |> ignore
)
let upperStripped = [List.head lowerStripped]
let killUpperDuplicates = lower |> List.iter ( fun letter -> (stripChars (List.head upperStripped) letter)::upperStripped |> ignore )
let strippedAll = List.head upperStripped
printfn "%s" strippedAll
But I couldn't get this working because I realized the consed lists weren't going anywhere (not to mention this is probably inefficient). The idea was that by doing it this way, once I parsed everything, the first element of the list would be the desired string.
I understand it may be strange asking a question I already have a solution to, but I feel like using mutable is just me not letting go of my Imperative habits (as I've read it should be rare to need to use it) and I want to more reinforce pure functional. So is there a better way to do this? Is the second solution a feasible route if I can somehow pipe the result somewhere?
You can use Seq.distinct to remove duplicates and retain ordering, so you just need to convert the list of strings to a single string, which can be done with String.concat "":
let distinctChars s = s |> String.concat ""
|> Seq.distinct
|> Array.ofSeq
|> System.String
If you run distinctChars ["John"; "James"; "Jack"], you will get back:
"Johnamesck"
This should do the trick:
let removeDuplicateCharacters strings =
// Treat each string as a seq<char>, flattening them into one big seq<char>
let chars = strings |> Seq.collect id // The id function (f(x) = x) is built in to F#
// We use it here because we want to collect the characters themselves
chars
|> Seq.mapi (fun i c -> i,c) // Get the index of each character in the overall sequence
|> Seq.choose (fun (i,c) ->
if i = (chars |> Seq.findIndex ((=) c)) // Is this character's index the same as the first occurence's index?
then Some c // If so, return (Some c) so that `choose` will include it,
else None) // Otherwise, return None so that `choose` will ignore it
|> Seq.toArray // Convert the seq<char> into a char []
|> System.String // Call the new String(char []) constructor with the choosen characters
Basically, we just treat the list of strings as one big sequence of characters, and choose the ones where the index in the overall sequence is the same as the index of the first occurrence of that character.
Running removeDuplicateCharacters ["John"; "James"; "Jack";] gives the expected output: "Johnamesck".
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.
Is there already a way to do something like a chooseTill or a foldTill, where it will process until a None option is received? Really, any of the higher order functions with a "till" option. Granted, it makes no sense for stuff like map, but I find I need this kind of thing pretty often and I wanted to make sure I wasn't reinventing the wheel.
In general, it'd be pretty easy to write something like this, but I'm curious if there is already a way to do this, or if this exists in some known library?
let chooseTill predicate (sequence:seq<'a>) =
seq {
let finished = ref false
for elem in sequence do
if not !finished then
match predicate elem with
| Some(x) -> yield x
| None -> finished := true
}
let foldTill predicate seed list =
let rec foldTill' acc = function
| [] -> acc
| (h::t) -> match predicate acc h with
| Some(x) -> foldTill' x t
| None -> acc
foldTill' seed list
let (++) a b = a.ToString() + b.ToString()
let abcdef = foldTill (fun acc v ->
if Char.IsWhiteSpace v then None
else Some(acc ++ v)) "" ("abcdef ghi" |> Seq.toList)
// result is "abcdef"
I think you can get that easily by combining Seq.scan and Seq.takeWhile:
open System
"abcdef ghi"
|> Seq.scan (fun (_, state) c -> c, (string c) + state) ('x', "")
|> Seq.takeWhile (fst >> Char.IsWhiteSpace >> not)
|> Seq.last |> snd
The idea is that Seq.scan is doing something like Seq.fold, but instead of waiting for the final result, it yields the intermediate states as it goes. You can then keep taking the intermediate states until you reach the end. In the above example, the state is the current character and the concatenated string (so that we can check if the character was whitespace).
A more general version based on a function that returns option could look like this:
let foldWhile f initial input =
// Generate sequence of all intermediate states
input |> Seq.scan (fun stateOpt inp ->
// If the current state is not 'None', then calculate a new one
// if 'f' returns 'None' then the overall result will be 'None'
stateOpt |> Option.bind (fun state -> f state inp)) (Some initial)
// Take only 'Some' states and get the last one
|> Seq.takeWhile Option.isSome
|> Seq.last |> Option.get