This compiles and works:
let rec HelloEternalWorld _ =
Console.ReadLine() |> printf "Input: %s\n"
HelloEternalWorld 0
HelloEternalWorld 0
This does not compile:
let rec HelloEternalWorld =
Console.ReadLine() |> printf "%s\n"
HelloEternalWorld
HelloEternalWorld
I try to understand why not?
All you're missing are parentheses, as it would compile if it were:
let rec HelloEternalWorld() =
Console.ReadLine() |> printf "%s\n"
HelloEternalWorld()
To define a function with no arguments you need the parentheses to distinguish the function from a simple value.
Please post the error messages you get, they say everything you need!
The value ... will be evaluated as part of its own definition.
Your code doesn't compile because you're declaring a recursive value (which doesn't exist) instead of a recursive function.
In order to make this a function, you'll have to write something like
let rec HelloEternalWorld() =
Console.ReadLine() |> printfn "%s"
HelloEternalWorld()
which is now a function of type unit -> unit.
Related
I have the following function that convert csv files to a specific txt schema (expected by CNTKTextFormat Reader):
open System.IO
open FSharp.Data;
open Deedle;
let convert (inFileName : string) =
let data = Frame.ReadCsv(inFileName)
let outFileName = inFileName.Substring(0, (inFileName.Length - 4)) + ".txt"
use outFile = new StreamWriter(outFileName, false)
data.Rows.Observations
|> Seq.map(fun kvp ->
let row = kvp.Value |> Series.observations |> Seq.map(fun (k,v) -> v) |> Seq.toList
match row with
| label::data ->
let body = data |> List.map string |> String.concat " "
outFile.WriteLine(sprintf "|labels %A |features %s" label body)
printf "%A" label
| _ ->
failwith "Bad data."
)
|> ignore
Strangely, the output file is empty after running in the F# interactive panel and that printf yields no printing at all.
If I remove the ignore to make sure that there are actual rows being processed (evidenced by returning a seq of nulls), instead of an empty file I get:
val it : seq<unit> = Error: Cannot write to a closed TextWriter.
Before, I was declaring the StreamWriter using let and disposing it manually, but I also generated empty files or just a few lines (say 5 out of thousands).
What is happening here? Also, how to fix the file writing?
Seq.map returns a lazy sequence which is not evaluated until it is iterated over. You are not currently iterating over it within convert so no rows are processed. If you return a Seq<unit> and iterate over it outside convert, outFile will already be closed which is why you see the exception.
You should use Seq.iter instead:
data.Rows.Observations
|> Seq.iter (fun kvp -> ...)
Apart from the solutions already mentioned, you could also avoid the StreamWriter altogether, and use one of the standard .Net functions, File.WriteAllLines. You would prepare a sequence of converted lines, and then write that to the file:
let convert (inFileName : string) =
let lines =
Frame.ReadCsv(inFileName).Rows.Observations
|> Seq.map(fun kvp ->
let row = kvp.Value |> Series.observations |> Seq.map snd |> Seq.toList
match row with
| label::data ->
let body = data |> List.map string |> String.concat " "
printf "%A" label
sprintf "|labels %A |features %s" label body
| _ ->
failwith "Bad data."
)
let outFileName = inFileName.Substring(0, (inFileName.Length - 4)) + ".txt"
File.WriteAllLines(outFileName, lines)
Update based on the discussion in the comments: Here's a solution that avoids Deedle altogether. I'm making some assumptions about your input file format here, based on another question you posted today: Label is in column 1, features follow.
let lines =
File.ReadLines inFileName
|> Seq.map (fun line ->
match Seq.toList(line.Split ',') with
| label::data ->
let body = data |> List.map string |> String.concat " "
printf "%A" label
sprintf "|labels %A |features %s" label body
| _ ->
failwith "Bad data."
)
As Lee already mentioned, Seq.map is lazy. And that's also why you were getting "Cannot write to a closed TextWriter": the use keyword disposes of its IDisposable when it goes out of scope. In this case, that's at the end of your function. Since Seq.map is lazy, your function was returning an unevaluated sequence object, which had closed over the StreamWriter in your use statement -- but by the time you evaluated that sequence (in whatever part of your code checked for the Seq of nulls, or in the F# Interactive window), the StreamWriter had already been disposed by going out of scope.
Change Seq.map to Seq.iter and both of your problems will be solved.
I want to time my functions, some of them use up to three parameters. Right now I'm using the same code below with some variations for the three.
let GetTime f (args : string) =
let sw = Stopwatch.StartNew()
f (args)
printfn "%s : %A" sw.Elapsed
I want to replace the three functions with this one.
let GetTime f ( args : 'T[]) =
let sW = Stopwatch.StartNew()
match args.Length with
| 1 -> f args.[0]
| 2 -> f (args.[0] args.[1])
printfn "%A" sW.Elapsed
()
But I'm getting an error of type mismatch, if I use the three functions it works. Is it possible to send the function as a parameter and use it like this?
Why not just do something like this?
let getTime f =
let sw = Stopwatch.StartNew()
let result = f ()
printfn "%A" sw.Elapsed
result
Assuming that f1, f2, and f3 are three functions that take respectively 1, 2, and 3 arguments, you can use the getTime function like this:
getTime (fun () -> f1 "foo")
getTime (fun () -> f2 "foo" "bar")
getTime (fun () -> f3 "foo" "bar" "baz")
However, if you just need to time some functions in FSI, this feature is already built-in: just type
> #time;;
and timing will be turned on.
It isn't possible for the compiler to know how many arguments will be passed at runtime, so the function f must satisfy both 'T -> unit and 'T -> 'T -> unit. This form also requires all arguments to be of the same type.
The following approach delays the function execution and may be suitable for your needs.
let printTime f =
let sw = Stopwatch.StartNew()
f() |> ignore
printfn "%A" sw.Elapsed
let f1 s = String.length s
let f2 s c = String.concat c s
printTime (fun () -> f1 "Test")
printTime (fun () -> f2 [| "Test1"; "Test2" |] ",")
You're probably thinking of passing a method group as an argument to GetTime, and then having the compiler decide which overload of the method group to call. That's not possible with any .NET compiler. Method groups are used for code analysis by compilers and tools such as ReSharper, but they are not something that actually exists at runtime.
If your functions take their arguments in tupled form, like these:
let f1 (s: string, b: bool) =
System.Threading.Thread.Sleep 1000
s
let f2 (n: int, s:string, dt: System.DateTime) =
System.Threading.Thread.Sleep 1000
n+1
then the implementation becomes trivial:
let Timed f args =
let sw = System.Diagnostics.Stopwatch.StartNew()
let ret = f args
printfn "Called with arguments %A, elapsed %A" args sw.Elapsed
ret
Usage:
f1
|> Timed // note, at this time we haven't yet applied any arguments
<| ("foo", true)
|> printfn "f1 done, returned %A"
f2
|> Timed
<| (42, "bar", DateTime.Now)
|> printfn "f2 done, returned %A"
However, if the functions take their arguments in curried form, like this:
let f1Curried (s: string) (b: bool) =
System.Threading.Thread.Sleep 1000
s
let f2Curried (n: int) (s:string) (dt: System.DateTime) =
System.Threading.Thread.Sleep 1000
n+1
it becomes a bit tricky. The idea is using standard operators (<|), (<||), and (<|||) that are intended to uncurry the arguments.
let Timed2 op f args =
let sw = System.Diagnostics.Stopwatch.StartNew()
let ret = op f args
printfn "Called with arguments %A, elapsed %A" args sw.Elapsed
ret
f1Curried
|> Timed2 (<||) // again, no arguments are passed yet
<| ("foo", true)
|> printfn "f1Curried done, returned %A"
f2Curried
|> Timed2 (<|||)
<| (42, "bar", DateTime.Now)
|> printfn "f2Curried done, returned %A"
So I have a function SolveEquasion that returns a pair float*float[]. What is the best way to print the number and the array and continue working with the array? I made the following code but it seems there is a better way
...
|> SolveEquasion
|> (fun (det, solution) -> printfn "Determinant = %f\nSolution = %A" det (Array.toList solution), solution )
|> snd
I don't think your solution can improved if you want to do this in a pipeline. Another approach is to use a let binding, along with splitting up the pipelined operations, to avoid having a function that acts like the love child of map and iter.
let (det, solution) = SolveEquasion
printfn "Determinant = %f\nSolution = %A" det (Array.toList solution)
//do something else with solution
I think the original solution is fine, and we can improve its clarity by giving your anonymous function the name I've seen it given in some other libraries based around pipelining higher-order functions: tap.
let tap f x =
f x
x
(1.0, [| 2.0; 3.0 |])
|> tap (fun (s, a) -> printfn "%A %A" s a)
|> snd
Well, for one thing you can skip the use of snd by returning a single value rather than a tuple from the previous function:
...
|> SolveEquasion
|> (fun (det, solution) ->
printfn "Determinant = %f\nSolution = %A" det (Array.toList solution)
solution )
I'd probably use Daniel's approach and just assign the value you want to print to a symbol using let. Alternatively, you could define a variant of printf that takes some arguments and returns one of them. I'm not sure if there is a general scheme how this should be done - for your example it would take a two-element tuple:
let mprintf fmt (a, b) =
Printf.kprintf (fun s -> printf "%s" s; (a, b)) fmt a b
Then you can write:
...
|> SolveEquasion
|> mprintfn "Determinant = %f\nSolution = %A"
|> snd |> // ... more stuff with solution
Why this piece of code is working on my fsi, but can't build the project? I am using vs2010 and F# 2.0 ...Any ideas that I am missing something?
let arg = [#"C:\Temp\bin"; #"C:\temp\xml"]
arg|> List.map(fun (s) -> printfn "%s" s)
getting the error telling that it was expecting int, how?
Error 1
Type mismatch. Expecting a string list -> int but given a string list -> 'a list
The type 'int' does not match the type ''a list'
C:\Users\Ebru\Documents\Visual Studio 2010\Projects\WinFind\WinFind\Program.fs
I'm guessing you actually wrote
[<EntryPoint>]
let Main(args) =
let arg = [#"C:\Temp\bin"; #"C:\temp\xml"]
arg|> List.map(fun (s) -> printfn "%s" s)
and an EntryPoint method (e.g. Main()) must return an int.
this snippet compiles on my machine, but the mapping seems weird. I think you really what to do this:
let arg = [#"C:\Temp\bin"; #"C:\temp\xml"]
arg|> List.iter (fun s -> printfn "%s" s)
which is the same as:
let arg = [#"C:\Temp\bin"; #"C:\temp\xml"]
arg|> List.iter (printfn "%s")
Regards,
forki
I'm trying to print the output of function only when it is true but so far all attempts have been unsuccsessful.
Something on the lines of:
let printFactor a b = if b then print_any((a,b))
Where b is a boolean and a is an integer.
When I try it I get:
val printFactor : 'a -> bool -> unit
Any suggestions?
EDIT:
To put things in context im trying to use this with a pipe operator. Lets say I have a function xyz that outputs a list of (int, bool). Id like to do something on these lines:
xyz |> printFactor
to print the true values only.
You could do e.g. this
let xyz() = [ (1,true); (2,false) ]
let printFactor (i,b) =
if b then
printfn "%A" i
xyz() |> List.iter printFactor
but it would probably be more idiomatic to do, e.g. this
xyz()
|> List.filter (fun (i,b) -> b)
|> List.iter (fun (i,b) -> printfn "%d" i)
that is, first filter, and then print.