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
Related
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 ""
How can I get get a list of names visible in the scope with FSC?
I tried this:
#r "../../packages/FSharp.Compiler.Service.16.0.2/lib/net45/FSharp.Compiler.Service.dll"
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.SourceCodeServices
do
let file = "TestFileName.fsx"
let checker = SourceCodeServices.FSharpChecker.Create()
let code =
"""
let testStr = "x"
t
"""
async{
let! options, _ = checker.GetProjectOptionsFromScript(file,code)
let! parseRes,checkAnser = checker.ParseAndCheckFileInProject(file, 0, code, options)
match checkAnser with
| FSharpCheckFileAnswer.Succeeded checkRes ->
let! decls =
checkRes.GetDeclarationListInfo(
Some parseRes, //ParsedFileResultsOpt
3 , //line
1 , //colAtEndOfPartialName
"t" , //lineText
[ "t" ] , //qualifyingNames
"" , //partialName
( fun _ -> [] ) //getAllSymbols: (unit -> AssemblySymbol list)
)
if Seq.isEmpty decls.Items then
printfn "*no declarations found*"
else
decls.Items
|> Seq.sortBy (fun d -> d.Name)
|> Seq.truncate 10
|> Seq.iter (fun d -> printfn "decl: %s" d.Name)
| _ -> failwithf "*Parsing did not finish... "
} |> Async.RunSynchronously
but it only prints "no declarations found". I would expect not only testStr but also all the other names that are available by default.
I did not find an example in the documentation.
qualifyingNames should be an empty list, it’s for dot separated prefix, excluding the last (possibly partial) ident. However, there is no a method in FCS that returns unfiltered list of names for scope, yet it’s really easy to add one.
With the answer of vasily-kirichenko and using the current FCS 17.0.1 I came up with this solution:
#r "../../packages/FSharp.Compiler.Service.17.0.1/lib/net45/FSharp.Compiler.Service.dll"
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.SourceCodeServices
do
let file = "TestFileName.fsx"
let checker = SourceCodeServices.FSharpChecker.Create()
let code =
"""
let testStr = "x"
testStr.
"""
async{
let! options, _ = checker.GetProjectOptionsFromScript(file,code)
let! parseRes,checkAnser = checker.ParseAndCheckFileInProject(file, 0, code, options)
match checkAnser with
| FSharpCheckFileAnswer.Succeeded checkRes ->
let! decls =
let partialName = PartialLongName.Empty 6 //use any location before before the dot to get all declarations in scope
//let partialName = PartialLongName.Empty 7 //use the loacation of the dot (7) to get memebers of string
checkRes.GetDeclarationListInfo(
Some parseRes, // ParsedFileResultsOpt
3 , // line
"testStr." , // lineText
partialName, // PartialLongName
( fun _ -> [] ) // getAllSymbols: (unit -> AssemblySymbol list)
)
if Seq.isEmpty decls.Items then
printfn "*no declarations found*"
else
decls.Items
|> Seq.sortBy (fun d -> d.Name)
|> Seq.truncate 10
|> Seq.iter (fun d -> printfn "decl: %s" d.Name)
| _ -> failwithf "*Parsing did not finish... "
} |> Async.RunSynchronously
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 want to create an F# function like List.find, but instead of searching for a single value, I want to search for any of the keys of a dictionary and return the corresponding dictionary value.
For example, this is a (poor) implementation of what I am trying to do.
let dict1=dict[(1,"A");(2,"B");(3,"C");(4,"D");(5,"E");(6,"F")]
let findInDict l =
let mutable found=false
let mutable value=""
for elem in l do
let f,v=dict1.TryGetValue(elem)
value<-if f && not found then v else value
found<-if not found then f else found
value
findInDict [9;2;5]
>
val dict1 : System.Collections.Generic.IDictionary<int,string>
val findInDict : l:seq<int> -> string
val it : string = "B"
What would be a functional equivalent?
A function for this almost feels like overkill. You can do this in one line using a list comprehension:
[for x in [9;4;5] do if dict1.ContainsKey x then yield dict1.[x]]
Edit:
After re-reading your question, I realized the above was not quite what you are looking for.
let rec findAValue l =
match l with
| [] -> None
| x::xs -> if dict1.ContainsKey x then Some(dict1.[x]) else findAValue xs
or more succinctly:
let rec findAValue = function
| [] -> None
| x::xs -> if dict1.ContainsKey x then Some(dict1.[x]) else findAValue xs
even more succinctly:
let findAValue = List.tryPick (fun x-> if dict1.ContainsKey x then Some(dict1.[x]) else None)
let highPerformanceFindAValue = List.tryPick (fun x-> match dict1.TryGetValue x with
| true, value->Some(value)
| _ -> None)
In the case where no value is found the result is None otherwise it's Some(value).
let findFirst l (dict: System.Collections.Generic.Dictionary<int, string>) =
let o = l |> List.tryFind (fun i -> dict.ContainsKey(i)) |> Option.map (fun k -> dict.[k])
match o with | None -> "" | Some(k) -> k
There are tons of ways to do this.
The obvious solution is to iterate, like you did:
let findInDict (d:IDictionary<'a, 'b>) l =
seq {
for key in l do
let f, v = d.TryGetValue(key)
if f then yield v
}
which is OK, I guess. It more or less mimics the typical step-wise approach.
You could rewrite this in terms of some sequence operators:
let findInDict1 (d:IDictionary<'a, 'b>) l =
Seq.filter (fun elem -> d.ContainsKey(elem)) l |> Seq.map (fun elem -> d.Item(elem))
which feels more functional, but is clearly doing way more work than it should be.
let findInDict2 (d:IDictionary<'a, 'b>) l =
Seq.choose(fun elem ->
let f,v = d.TryGetValue(elem)
if f then Some(v) else None) l
The last one makes the most sense in that we're only ever accessing the dictionary once per key and choose will do all the heavy lifting for us under the hood.
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