I'm attempting to draw in a non-customized (I mean, simply creating an instance of the default form class, not an instance of a deriving class I can create) System.Windows.Forms.Form in F#.
I had created a custom form, but I didn't need nor want a new complex structure like that, so I removed it, and it simplified the code a lot; so much, that it stopped to show an image.
The problem must be in the function I created to draw, that is in another F# project. I've created it (function conect) to connect points in the order they are provided, unlike System.Drawing.Graphics.DrawLines, that draws lines between the points in some other order why haven't noticed yet (maybe right to left, top to bottom, as the points are represented).
Programa.fs relevant code snippet:
let pen = new Pen(brush = Brushes.Black, width = 1.0f)
let original =
([|new PointF(50.0f, 50.0f); new PointF(100.0f, 50.0f)|])
use form1 = new Form(Width = 400, Height = 400, Text = "Fractais (Teste - Windows Forms)")
form1.Paint.Add(
fun e -> // (1)
original
|> List.ofArray
|> Base.applyFractal 1uy Base.fractalFunc1
|> Base.conect e.Graphics pen)
If in the lambda expression instead of what's written there was e.Graphics.DrawLines(pen, original), it would draw a simple line between the points in the list.
Here's the troublemaker method, in Base.fs, across the solution:
let conect (gr:Graphics) (pen:Pen) (points:PointF list) =
let rec usefulFunc (gr:Graphics) (pen:Pen) (points:PointF list) prevPoint =
match points with
| [] -> ()
| point :: remainings ->
gr.DrawLine (pen, prevPoint, point)
usefulFunc gr caneta remainings.Tail remainings.Head
usefulFunc gr pen points.Tail points.Head
And the called (from the form initialization snippet) and relevant methods' signatures, in Base.fsi (I could give you all of the full methods' implementation, but it would take a lot of space, and this is probably for you already becoming a long question to read):
val fractalFunc1 : points:PointF list -> PointF list
val applyFractal : stepNumber:byte -> fractalFunc:(PointF list -> PointF list) -> points:PointF list -> PointF list
val conect : gr:Graphics -> pen:Pen -> points:PointF list -> unit
For this specific problem, my search results were none. I'd like to know how can I make the function conect work.
Thanks in advance.
You have one error in conectar.
fUtil gr caneta resto.Tail resto.Head
Should be
fUtil gr caneta resto ponto
You're already matching the head and tail inside of the match statement.
The following code draws a line for me. I didn't have to modify much.
open System.Drawing
open System.Windows.Forms
let caneta = new Pen(brush = Brushes.Black, width = 1.0f)
let original =
([|new PointF(50.0f, 50.0f); new PointF(100.0f, 50.0f)|])
let form1 = new Form(Width = 400, Height = 400, Text = "Fractais (Teste - Windows Forms)")
let conectar (gr:Graphics) (caneta:Pen) (pontos:PointF list) =
let rec fUtil (gr:Graphics) (caneta:Pen) (pontos:PointF list) pontoAnt =
match pontos with
| [] -> ()
| ponto :: resto ->
gr.DrawLine (caneta, pontoAnt, ponto)
fUtil gr caneta resto ponto
fUtil gr caneta pontos.Tail pontos.Head
form1.Paint.Add(
fun e -> // (1)
original
|> List.ofArray
//|> aplicFractal 1uy Base.funcFractal1
|> conectar e.Graphics caneta)
form1.Show()
Application.Run(form1)
Related
I used the following code, to write a mylist in a *.txt file.
let outFile = new StreamWriter("4.txt")
let writeNumbersInFile (numbersToupleList)=function
|[] -> ()
|head::tail -> outFile.WriteLine(string (head))
let mylist= [1;2;3;4]
writeNumbersInFile mylist
outFile.Close()
This code succesfully creates a 4.txt, but when I open it, there is nothing there, however i expect to see
1
2
3
4
this should work - not how I removed the numbersToupleList argument - you don't need it (and you did not use it) because function has an implicit argument:
let outFile = new StreamWriter("4.txt")
let rec writeNumbersInFile =
function
|[] -> ()
|head::tail ->
outFile.WriteLine (string head)
writeNumbersInFile tail
let mylist= [1;2;3;4]
writeNumbersInFile mylist
outFile.Flush()
of course the recursion is not really needed (there are higher-order-functions to do this for you) - you can use Seq.iter or List.iter instead:
let outFile = new StreamWriter("4.txt")
let writeNumbersInFile =
Seq.iter (string >> outFile.WriteLine)
let mylist= [1;2;3;4]
writeNumbersInFile mylist
outFile.Flush()
and now you should probably refactor this into something like this - note that I had to give the type of file so that F# can decide which StreamWriter constructor to use and that this will dispose the StreamWriter as soon as the work is done in writeNumbersInFile (the use) - so you will not keep file-handles around till the GC kicks in:
let writeNumbersInFile (file : string) (numbers : int seq) =
use outFile = new StreamWriter (file)
Seq.iter (string >> outFile.WriteLine) numbers
outFile.Flush ()
[1;2;3;4]
|> writeNumbersInFile "4.txt"
I use F# once every few months or so and in between it seems I forget everything, so I hope you'll excuse my ignorance. My code below is pulling data from Yahoo. It's a good example that represents what I need to do. The first row returned has the column headings. I need to take the data (the tail of the list) and insert it into the database. What's the best way to generate an insert statement based on the column headings returned (the column headings match the database column names)?
In the example below dataWithHeaders.[0] will contain "Date,Open,High,Low,Close,Volume,Adj Close." Should I just take that string and put brackets around the headings to create the insert? Then in insertData add the value as a parameter? Is there a more elegant solution?
let url = System.String.Format("http://ichart.finance.yahoo.com/table.csv?s={0}&g=d&ignore=.csv", "FB")
let splitLineIntoArray (line : string) =
line.Split(",".ToCharArray())
let insertData (data : string[]) =
// insert data
()
let client = new WebClient()
let dataWithHeaders =
client.DownloadString(url).Split(Environment.NewLine.ToCharArray())
let data =
dataWithHeaders
|> Array.toList
|> List.tail
|> List.map(splitLineIntoArray)
|> List.iter insertData
If you're loading the data into SQL Server you can use this excellent CSV reader (free) and the SqlBulkCopy class. It's simple and efficient.
let loadStockPrices ticker =
use client = new WebClient()
let url = sprintf "http://ichart.finance.yahoo.com/table.csv?s=%s&g=d&ignore=.csv" ticker
use stringReader = new StringReader(client.DownloadString(url))
use csvReader = new CsvReader(stringReader, hasHeaders=true)
use con = new SqlConnection("<connection_string>")
con.Open()
use bulkCopy = new SqlBulkCopy(con, DestinationTableName="<destination_table>")
bulkCopy.WriteToServer(csvReader)
The destination table should have the same columns as the incoming data (OHLC, etc).
EDIT: Type Providers might be a good way to go, but SqlBulkCopy is def. known for its simplicity.
Type Provider Code for Insert: http://msdn.microsoft.com/en-us/library/hh361033(v=vs.110).aspx#BKMK_UpdateDB
type dbSchema = SqlDataConnection<"Data Source=MYSERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;">
let db = dbSchema.GetDataContext()
// Enable the logging of database activity to the console.
db.DataContext.Log <- System.Console.Out
let newRecord = new dbSchema.ServiceTypes.Table1(Id = 100,
TestData1 = 35,
TestData2 = 2.0,
Name = "Testing123")
let newValues =
[ for i in [1 .. 10] ->
new dbSchema.ServiceTypes.Table3(Id = 700 + i,
Name = "Testing" + i.ToString(),
Data = i) ]
// Insert the new data into the database.
db.Table1.InsertOnSubmit(newRecord)
db.Table3.InsertAllOnSubmit(newValues)
try
db.DataContext.SubmitChanges()
printfn "Successfully inserted new rows."
with
| exn -> printfn "Exception:\n%s" exn.Message
I did something similar. Actually this code I wrote while watching Luca Bolognese give a presentation on F#. This actually will scrape yahoo's feed and return the standard dev. and variance on stock prices.
Full project here: https://github.com/djohnsonm/Stock-Ticker-App
open System.Net
open System.IO
let internal loadPrices ticker = async {
let url = #"http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=6&e=22&f=2011&g=d&a=2&b=13&c=1986&ignore=.csv"
let req = WebRequest.Create(url)
let resp = req.GetResponse()
let stream = resp.GetResponseStream()
let reader = new StreamReader(stream)
let csv = reader.ReadToEnd()
let prices =
csv.Split([|'\n'|])
|> Seq.skip 1
|> Seq.map (fun line -> line.Split([|','|]))
|> Seq.filter(fun values -> values |> Seq.length = 7)
|> Seq.map(fun values ->
System.DateTime.Parse(values.[0]),
float values.[6])
return prices}
type StockAnalyzer (lprices, days) =
let prices =
lprices
|> Seq.map snd
|> Seq.take days
static member GetAnalyzers (tickers, days) =
tickers
|> Seq.map loadPrices
|> Async.Parallel
|> Async.RunSynchronously
|> Seq.map (fun prices -> new StockAnalyzer(prices, days))
member s.Return =
let lastPrice = prices |> Seq.nth 0
let startPrice = prices |> Seq.nth(days-1)
lastPrice / startPrice - 1.
member s.StdDev =
let logRets =
prices
|> Seq.pairwise
|> Seq.map (fun (x,y) -> log(x/y))
let mean = logRets |> Seq.average
let sqr x = x * x
let var = logRets |> Seq.averageBy (fun r -> sqr (r-mean))
sqrt var
I recently asked this question:
Replay Recorded Data Stream in F# and combined that code with a subset of the functionality I found here:
http://www.mattssoftwareblog.com/?p=271
which combined looks like this:
#r "System.Reactive"
#r "System.CoreEx"
#r "FSharp.PowerPack"
#r "WindowsBase"
#r "PresentationCore"
#r "PresentationFramework"
#r "System.Xaml"
#r "System.Interactive.dll"
open System
open System.Linq
open System.Collections.Generic
open System.Net
open System.IO
open System.Threading
open System.Windows
open System.Windows.Input
open System.Windows.Controls
open System.Windows.Shapes
open System.Windows.Media
open System.Xaml
I need to use the events generated here (which came from my earlier SO question):
let prices = [ (0, 10.0); (1000, 10.5); (500, 9.5); (2500, 8.5); (500, 10.0); (1000, 10.5); (500, 9.5); (2500, 8.5) ]
let evt = new Event<float>()
async { for delay, price in prices do
do! Async.Sleep(delay)
evt.Trigger(price) }
|> Async.StartImmediate
evt.Publish.Add(printfn "Price updated: %A")
to use as the data source for the line which is randomly created here (the code below comes from the blog article I mentioned):
let create f =
Observable.Create<_>(fun x ->
f x
new System.Action((fun () -> ())))
let fromEvent (event:IEvent<_,_>) = create (fun x -> event.Add x.OnNext)
// Random Walker
let rand = Random()
let mutable m = 0.
let randomWalker() =
m <- m + (rand.NextDouble() * 10.) - 5.
m
let timer = new System.Timers.Timer()
timer.Interval <- 100.
let timerObs = (timer.Elapsed |> fromEvent).Select(fun _ -> randomWalker())
let chartWindow = new Window(Height = 600., Width = 600.)
let canvas = new Canvas()
chartWindow.Content <- canvas
chartWindow.Show()
let line xs =
let segs =
seq { for x , y in xs |> List.tail ->
LineSegment(Point(x,y), true) :> PathSegment }
let (sx, sy) = xs |> List.head
PathGeometry([PathFigure(Point(sx,sy), segs, false)])
let plot xs (path:Path) =
let now = DateTime.Now
let timeSpan = TimeSpan(0,1,0)
let width = 600.
let height = 600.
let pts = xs |> List.map (fun (x:Timestamped<float>) ->
(600.-(now - (x.Timestamp.DateTime)).TotalMilliseconds * 600. / timeSpan.TotalMilliseconds),x.Value + 300.)
path.Dispatcher.BeginInvoke(new SendOrPostCallback(fun pts -> path.Data <- line (pts :?> (float*float)list)), pts) |> ignore
let trailing (timespan:TimeSpan) (obs:IObservable<'
a>) =
obs.Timestamp()
.Scan([], fun ys x ->
let now = DateTime.Now
let timespan = timespan
x :: (ys |> List.filter (fun x -> (now - x.Timestamp.DateTime) < timespan)))
.Where(fun xs -> xs |> List.length > 1)
// Main Path
let mainPath = new Path(Stroke=Brushes.Blue, StrokeThickness=1.)
canvas.Children.Add(mainPath)
let trailingRandomsSub = (timerObs |> trailing (TimeSpan.FromSeconds(60.))).Subscribe(fun xs -> plot xs mainPath)
timer.Start()
If you paste this into an interactive session you will see a blue line emerge which is generated randomly and not using my new evt Event. I guess my confusion is not understanding how to make and use an Observable from my evt. Basically, how can I make evt my data source for the blue line?
Thanks in advance,
Bob
In F#, the IEvent<'T> interface inherits from IObservable<'T>. This means that you can use F# events in any place where an observable is expected.
The last bit of your application (that takes the event, adds time stamps, uses Scan to get lists containing the items generated so far and plots the progress) can be written like this:
let trailingRandomsSub =
evt.Publish.Timestamp()
|> Observable.scan (fun l e -> e::l) []
|> Observable.add (fun xs -> plot xs mainPath)
F# provides wrappers for some of the Rx functions, so you can use Observable.scan, which has a bit more F#-friendly syntax. Observable.add is just another syntax for Subscribe.
The key difference between F# events and observables is that observables start when you attach a handler. On the other hand, the F# event that you create using Async.StartImmediate starts immediately when the StartImmediate method is called (this means - to get the sample working, you need to evaluate everything at once, or write a function that starts the event).
I am trying to populate the values of an autocomplete dynamically. I get a list of SearchResult tuples, where per tuple the first string is the KEY and the list of strings are the display text that need to display per column. The assumption is that of any list of SearchResult all rows will contain the same amount of items in the display text list. I really would like to be able to bind to the values of the display text list by ordinal...
a simplified example would take data like this:
[
("MSFT.OQ", ["MSFT.OQ"; "Microsoft"; "Nasdaq"]) ;
("GOOG.OQ", ["GOOG.OQ"; "Google"; "Nasdaq"]);
]
and display:
MSFT.OQ Microsoft Nasdaq
GOOG.OQ Google Nasdaq
but I am seeing something like:
["MSFT.OQ"; "Microsoft"; "Nasdaq"] ["MSFT.OQ"; "Microsoft"; "Nasdaq"] ["MSFT.OQ"; "Microsoft"; "Nasdaq"]
["GOOG.OQ"; "Google"; "Nasdaq"] ["GOOG.OQ"; "Google"; "Nasdaq"] ["GOOG.OQ"; "Google"; "Nasdaq"]
The entire list is ending up in each column, so I am thinking my binding is off.
My sample code (attempted to be simplified from a more complex model):
type SearchResult = (string * string list)
type Template() as this =
inherit Page
[<DefaultValue>]
val mutable acbTickerSearch : AutoCompleteBox
do
this.acbTickerSearch = this ? acbTickerSearch
this.display Some(this.getSampleResults())
member private this.getSampleResults() =
[
("MSFT.OQ", ["MSFT.OQ"; "Microsoft"; "Nasdaq"]) ;
("GOOG.OQ", ["GOOG.OQ"; "Google"; "Nasdaq"]);
("IBM", ["IBM"; "International Business Machines"; "NYSE"]);
("AKAM.OQ", ["AKAM.OQ"; "Akamai"; "Nasdaq"]);
]
member this.display (results: SearchResult list option) =
let build(result: SearchResult) =
// if we haven't built the bindings yet lets do so
if this.tickerSearchDataGrid = null then
// create a grid
this.tickerSearchDataGrid <- new DataGrid()
this.tickerSearchDataGrid.AutoGenerateColumns <- false
let addColumn i (item: string) =
let col = new DataGridTextColumn()
let binding = System.Windows.Data.Binding()
// LOOK HERE: attempting to bind to an indexer... not working so well,,
binding.Path <- PropertyPath([i])
col.Binding <- binding
this.tickerSearchDataGrid.Columns.Add(col)
i + 1
result
// the second portion of the tuple, is a list that
// needs to be displayed, wach item in its own column
|> snd
// should probably be List.iteri
|> List.fold addColumn 0
// don't need this with List.iteri
|> ignore
let displayResults (resultLst: SearchResult list) =
// create a list of lists, throwing away the "key" portion of the tuple
// potentially a bug I need toget around...
let lst =
resultLst
|> List.map (fun (r: SearchResult) -> snd r)
// bind to the data source
this.tickerSearchDataGrid.ItemsSource <- lst
this.tickerSearchDataGrid.HeadersVisibility <- DataGridHeadersVisibility.None
this.acbTickerSearch.ItemsSource <- [this.tickerSearchDataGrid]
this.acbTickerSearch.IsDropDownOpen <- true
match results with
| None -> ()
| Some r ->
// set the number of columns based on the results,
// assume all tuples will have an equal number of values,
// we only need the head to determine the columns then
build <| List.head r
// bind the results
displayResults r
Thank you...
suprisingly (to me at least) these return the same results:
binding.Path <- PropertyPath([])
binding.Path <- PropertyPath("")
binding.Path <- PropertyPath([0].[0])
not making much sense to me...
I don't actually know, but I would try
binding.Path <- PropertyPath(sprintf "[%d]" i)
(e.g. put it inside a quoted string), based on reading this:
http://msdn.microsoft.com/en-us/library/cc645024%28VS.95%29.aspx
adopting this:
http://www.scottlogic.co.uk/blog/colin/2009/04/binding-a-silverlight-datagrid-to-dynamic-data-via-idictionary/
worked:
type RowIndexConverter() =
interface IValueConverter with
member this.Convert(value, targetType, parameter, culture) =
let row = value :?> string list ;
let index: int = parameter :?> int;
row.[index] :> obj;
member this.ConvertBack(value, targetType, parameter, culture) = raise <| NotImplementedException()
and in my code replace the binding with
let binding = System.Windows.Data.Binding()
binding.Path <- PropertyPath([])
binding.Converter <- RowIndexConverter()
binding.ConverterParameter <- i
I guess it is an ok solution. Hopefully Brains (was that a typo or divine intervention) will work as well, its a lot more straight forward.
I have two functions, horizontal and vertical, for laying out controls. They work like this:
let verticalList = vertical [new TextBlock(Text = "one");
new TextBlock(Text = "two");
new TextBlock(Text = "three")]
Now verticalList is a control that displays the three textblocks vertically:
one
two
three
Here are the definitions:
let horizontal controls =
let wrap = new WrapPanel() in
List.iter (wrap.Children.Add >> ignore) controls ;
wrap
let vertical controls =
let stack = new StackPanel() in
List.iter (stack.Children.Add >> ignore) controls ;
stack
A problem occurs when I combine different types:
let foo = vertical [new TextBlock(Text = "Title"); vertical items]
This complains that the elements of the list are not of the same type. That is true, but they have a common supertype (UIElement).
I know I can use :> UIElement to upcast both items in the list, but this is an ugly solution. Can F# infer the common supertype. If not, why not?
It would be great if the nice looking
vertical [X; Y; Z]
doesn't have to become
vertical [(X :> UIElement); (Y :> UIElement); (Z :> UIElement)]
There are a few ways, including
type Animal() = class end
type Cat() =
inherit Animal()
type Dog() =
inherit Animal()
let animals1 : list<Animal> = [upcast new Cat(); upcast new Dog()]
let animals2 = ([upcast new Cat(); upcast new Dog()] : list<Animal>)
let animals3 = [(new Cat() :> Animal); upcast new Dog()]
animals1: type annotation at the var declaration, upcast each element
animals2: type annotation on the list expression, upcast each element
animals3: explicit type on first element, upcast rest
In a future version of F#, the upcast is likely to become unnecessary.
(See also http://cs.hubfs.net/forums/thread/9953.aspx , but nothing 'new' there.)
If you agree to sacrifice type safety for readability here you are the workaround:
open System
let to_list (tuple: Object) =
let rec list_after_index (n: int) =
let prop = tuple.GetType().GetMethod("get_Item"+n.ToString())
match prop with
| null -> []
| _ -> prop.Invoke(tuple, [||]) :: list_after_index(n+1)
match tuple with
| :? unit -> []
| _ when tuple.GetType().FullName.Contains(".Tuple`") -> list_after_index(1)
| _ -> [tuple]
then you can use it like that:
> to_list ();;
val it : obj list = []
> to_list (1);;
val it : obj list = [1]
> to_list([], 1, "2", 3.0);;
val it : obj list = [[]; 1; "2"; 3.0]
e.g. inside your vertical function.
I like my previous answer better, but building on that, if you have an app where you're constantly creating non-homogenous lists of animals, you can always do something like this:
let Animalize (x:Animal) = x // define a function to upcast
let animals4 = [ Animalize <| new Cat(); Animalize <| new Dog()]
// or even
let (~++) = Animalize // define a prefix operator to upcast (~ makes it prefix)
let animals5 = [ ++ new Cat(); ++ new Dog()]
The last is almost certainly an abuse of operators, unless you're in a very highly specialized domain and you constantly need some coercion like this and you're willing to sacrifice inherent readability for terseness.