I want to read all the lines from the file and implement acquired the following:
let s1 = File.ReadAllText("\\test.txt")
let splitArr = s1.Split[|' '|] |> Array.toList
let checkList = check splitArr
let final = String.concat " " checkList |> toLower |> makeUpper
Console.Write(final)
I even tried to looping through the file but perhaps did wrong:
let myfile = File.OpenText("\\test.txt")
let fileLoop() =
let s1 = myfile.ReadLine()
let splitArr = s1.Split[|' '|] |> Array.toList
let checkList = check splitArr
let final = String.concat " " checkList |> toLower |> upper
Console.Write(s1)
while fileLoop() do ignore None
Your existing code bears little resemblance with your textual description of what you are trying to achieve. Ignoring that, a simple observation to make your current code work: You are using File.ReadAllText, which returns a giant blob of text - but you are afterwards assuming it comes already separated by lines. Use one of the methods in the File module that returns the text linewise, that is ReadLines or ReadAllLines. Try the following:
File.ReadLines("\\test.txt")
|> Seq.iter (fun s1 ->
let splitArr = s1.Split[|' '|] |> Array.toList
let checkList = check splitArr
let final = String.concat " " checkList |> toLower |> makeUpper
Console.WriteLine(final)
)
Or making heavier use of pipes:
File.ReadLines "\\test.txt"
|> Seq.iter (fun s1 ->
s1.Split [|' '|]
|> Array.toList
|> check
|> String.concat " "
|> toLower
|> makeUpper
|> Console.WriteLine
)
Related
So I am working on this assignment where I have to reverse the document but it does not work as intended. If I run my code on a file like this
hello
world
I will get
dlrow
olleh
But what I really need is the following:
world
hello
So I need to reverse it line by line but not for every letter. I dont know how to tackle this problem so I need someone who can push me in the right direction. I think that I need to do something with my "q"
My Code is:
let readFile (filename : string) : string option =
try
let reader = System.IO.File.OpenText filename
Some (reader.ReadToEnd ())
with _ -> None
let tac (filenames : string list) : string option =
try
let q = List.map readFile filenames |> List.choose id |> String.concat ", " |> Seq.toList |> List.rev
(Some (System.String.Concat(Array.ofList (q))))
with _ -> None
As Daniel said, ...divide and conquer
Break it into smaller steps and solve one at a time
When you know what it takes to solve it,
you can then choose to improve it, 'refactor' it
to optimize
or add error handling
or specialize/rewrite a function
...etc
open System
open System.IO
// V1
File.ReadLines "./my.txt"
|> Seq.toList
|> List.rev
|> List.map (fun x -> sprintf "%s%s" x Environment.NewLine)
|> List.fold (+) ""
|> printf "%A\n"
... how do you reverse it without a for loop ?
open System
open System.IO
// v2
let rec reverse =
function
| [] -> []
| h::t -> (reverse t)#[h]
let join = List.fold (+) ""
let newLine x = sprintf "%s%s" x Environment.NewLine
// look , I'm a 'compositor' ! :) ♪
let chain = reverse >> List.map newLine >> join
File.ReadLines "./my.txt"
|> Seq.toList
|> chain
|> printf "%A\n"
... what if it doesn't exists ?
// V3
open System
open System.IO
let rec reverse =
function
| [] -> []
| h::t -> (reverse t)#[h]
let join = List.fold (+) ""
let newLine x = sprintf "%s%s" x Environment.NewLine
// 👣
let chain = reverse >> List.map newLine >> join
// NEW
let readLines path =
if File.Exists path then
// 🐛: What if its Binary or something line that ?
File.ReadLines path |> Seq.toList
else
[]
readLines "./my.txt"
|> chain
|> printf "%A\n"
[Edit]
Optional string version
// v4
open System
open System.IO
// New
let inline maybe test f x =
if test x then x |> f |> Some else None
// New
let inline ifSome f = maybe Option.isSome f
// New
let inline ifNone f = maybe Option.isNone f
// New
let ifExists = maybe File.Exists
let rec reverse =
function
| [] -> []
| h :: t -> (reverse t) # [ h ]
let join = List.fold (+) ""
let newLine x = sprintf "%s%s" x Environment.NewLine
let chain =
reverse
>> List.map newLine
>> join
let readLines path =
(File.ReadLines path)
|> Seq.toList
// usage '$ fsi reverse.fsx "my.txt"'
fsi.CommandLineArgs
|> Seq.skip 1
|> Seq.head // expecting "my.txt"
|> ifExists readLines
|> Option.map chain
|> ifSome (fun x -> printf "%A\n" x)
|> ifNone (fun x -> printf "None! \n")
I am trying to use FAKE for automating my project builds. In the process, I wrote the following F# code -
open System
open System.IO
module FileUtils =
type BuildConfiguration = {Name:string; Directory:string}
let DebugBuildConfiguration = {Name = "Debug"; Directory = #"\bin\Debug"}
let ReleaseBuildConfiguration = {Name = "Release"; Directory = #"\bin\Release"}
let StagingBuildConfiguration = {Name = "Staging"; Directory = #"\bin\Staging"}
let codeDir = #"C:\source-control\s4sold\src"
let sourceDirs = [| "co"; "lo"; "mo"; "po"; "pr"; "pro"; "re"; "ro" |]
let GetAllBuildConfigurationDirs buildConfiguration =
let allSourceDirs = sourceDirs
|> Seq.map (fun i -> Path.Combine(codeDir, i))
|> Seq.map (fun d -> Directory.GetDirectories(d))
|> Array.concat
allSourceDirs |> printf "%A"
List.ofArray allSourceDirs
|> List.map (fun i -> Path.Combine(i, buildConfiguration.Directory))
// |> Seq.toArray
Now the problem I am facing is that when I print allSourceDirs, it correctly prints the directories under my /src folder. But when I run GetAllBuildConfigurationDirs, all I get is an array of "\bin\Debug", which means it doesn't take the output of allSourceDirs into consideration.
I am really at a loss to understand what's happening here. I am an F# newbie, but know some Clojure. Also how can I use Seq.map over a seq of seqs (so that I might avoid the Array.concat call)?
The problem is the way you are combining paths, you have to remove the \ at the beginning, just write #"bin\Debug"
Also you can combine both maps into one, since map f |> map g is equivalent to map (f |> g) and instead of map + concat you can use collect.
Here's your original code with these corrections:
open System
open System.IO
module FileUtils =
type BuildConfiguration = {Name:string; Directory:string}
let DebugBuildConfiguration = {Name = "Debug"; Directory = #"bin\Debug"}
let ReleaseBuildConfiguration = {Name = "Release"; Directory = #"bin\Release"}
let StagingBuildConfiguration = {Name = "Staging"; Directory = #"bin\Staging"}
let codeDir = #"C:\source-control\s4sold\src"
let sourceDirs = [| "co"; "lo"; "mo"; "po"; "pr"; "pro"; "re"; "ro" |]
let allSourceDirs = sourceDirs
|> Array.collect (fun i -> Path.Combine(codeDir, i) |> Directory.GetDirectories)
allSourceDirs |> printf "%A"
List.ofArray allSourceDirs
|> List.map (fun i -> Path.Combine(i, buildConfiguration.Directory))
// |> Seq.toArray
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
I am new to F# and trying to solve this Kata
The file football.dat contains the results from the English Premier League for 2001/2. The columns labeled ‘F’ and ‘A’ contain the total number of goals scored for and against each team in that season (so Arsenal scored 79 goals against opponents, and had 36 goals scored against them). Write a program to print the name of the team with the smallest difference in ‘for’ and ‘against’ goals.
When save the file and and the read it using File.ReadAllLines, my solutons works:
open System.IO
open System
let split (s:string) =
let cells = Array.ofSeq(s.Split([|' '|],StringSplitOptions.RemoveEmptyEntries))
(cells.[1], int cells.[6], int cells.[8])
let balance t =
let (_,f,a) = t
-(f-a)
let lines = List.ofSeq(File.ReadAllLines(#"F:\Users\Igor\Downloads\football.dat"));;
lines
|> Seq.skip 5
|> Seq.filter (fun (s:string) -> s.Split([|' '|],StringSplitOptions.RemoveEmptyEntries).Length = 10)
|> Seq.map split
|> Seq.sortBy balance
|> Seq.take 1
|> Seq.map (fun (n,_,_) -> printfn "%s" n)
but when instead of reading the file I download it using WebClient and split lines the rest of the code does not work. The sequence is the same length but F# Interactive does not show the elements and prints no output. The code is
open System.Net
open System
let split (s:string) =
let cells = Array.ofSeq(s.Split([|' '|],StringSplitOptions.RemoveEmptyEntries))
(cells.[1], int cells.[6], int cells.[8])
let balance t =
let (_,f,a) = t
-(f-a)
let splitLines (s:string) =
List.ofSeq(s.Split([|'\n'|]))
let wc = new WebClient()
let lines = wc.DownloadString("http://pragdave.pragprog.com/data/football.dat")
lines
|> splitLines
|> Seq.skip 5
|> Seq.filter (fun (s:string) -> s.Split([|' '|],StringSplitOptions.RemoveEmptyEntries).Length = 10)
|> Seq.map split
|> Seq.sortBy balance
|> Seq.take 1
|> Seq.map (fun (n,_,_) -> printfn "%s" n)
What is the difference? List.ofSeq(File.ReadAllLines..) retuns a sequence and downloading the file from the internet and splitting it by \n returns the same sequence
The last line should use Seq.iter instead of Seq.map and there are too many spaces in the expression that splits each line.
With these corrections it works ok:
open System.Net
open System
open System.IO
let split (s:string) =
let cells = Array.ofSeq(s.Split([|' '|],StringSplitOptions.RemoveEmptyEntries))
(cells.[1], int cells.[6], int cells.[8])
let balance t =
let (_,f,a) = t
-(f-a)
let splitLines (s:string) =
List.ofSeq(s.Split([|'\n'|]))
let wc = new WebClient()
let lines = wc.DownloadString("http://pragdave.pragprog.com/data/football.dat") |> splitLines
let output =
lines
|> Seq.skip 5
|> Seq.filter (fun (s:string) -> s.Split([|' '|],StringSplitOptions.RemoveEmptyEntries).Length = 10)
|> Seq.map split
|> Seq.sortBy balance
|> Seq.take 1
|> Seq.iter (fun (n,_,_) -> Console.Write(n))
let stop = Console.ReadKey()
That URL is returning an HTML page, not a raw data file, could that be what is causing your problems?
Also it is usually good to verify what delimiter the page is using for newlines. That one is using 0x0A which is \n, but sometimes you will find \r\n or rarely \r.
EDIT:
Also you appear to be using map to handle printing, this isn't a good way to do it. I know I am having difficulty in general getting your sample to show an output when executing all at once.
I would recommend mapping to n and print some other way, such as using Seq.head.
let private GetDrives = seq{
let all=System.IO.DriveInfo.GetDrives()
for d in all do
//if(d.IsReady && d.DriveType=System.IO.DriveType.Fixed) then
yield d
}
let valid={'A'..'Z'}
let rec SearchRegistryForInvalidDrive (start:RegistryKey) = seq{
let validDrives=GetDrives |> Seq.map (fun x -> x.Name.Substring(0,1))
let invalidDrives= Seq.toList validDrives |> List.filter(fun x-> not (List.exists2 x b)) //(List.exists is the wrong method I think, but it doesn't compile
I followed F#: Filter items found in one list from another list but could not apply it to my problem as both the solutions I see don't seem to compile. List.Contains doesn't exist (missing a reference?) and ListA - ListB doesn't compile either.
open System.IO
let driveLetters = set [ for d in DriveInfo.GetDrives() -> d.Name.[0] ]
let unused = set ['A'..'Z'] - driveLetters
Your first error is mixing between char and string, it is good to start with char:
let all = {'A'..'Z'}
let validDrives = GetDrives |> Seq.map (fun x -> x.Name.[0])
Now invalid drive letters are those letters which are in all but not in validDrives:
let invalidDrives =
all |> Seq.filter (fun c -> validDrives |> List.forall ((<>) c))
Since validDrives is traversed many times to check for membership, turning it to a set is better in this example:
let all = {'A'..'Z'}
let validDrives = GetDrives |> Seq.map (fun x -> x.Name.[0]) |> Set.ofSeq
let invalidDrives = all |> Seq.filter (not << validDrives.Contains)