2
// Christohper James Halse Rogers <raof@ubuntu.com>
4
// Copyright © 2014 Christopher James Halse Rogers <raof@ubuntu.com>
6
// This program is free software: you can redistribute it and/or modify
7
// it under the terms of the GNU Lesser General Public License as published by
8
// the Free Software Foundation, either version 3 of the License, or
9
// (at your option) any later version.
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU Lesser General Public License for more details.
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with this program. If not, see <http://www.gnu.org/licenses/>.
19
// This sample will guide you through elements of the F# language.
21
// *******************************************************************************************************
22
// To execute the code in F# Interactive, highlight a section of code and press Alt-Enter or right-click
23
// and select "Execute in Interactive". You can open the F# Interactive Window from the "View" menu.
24
// *******************************************************************************************************
26
// For more about F#, see:
29
// For additional templates to use with F#, see the 'Online Templates' in Visual Studio,
30
// 'New Project' --> 'Online Templates'
32
// For specific F# topics, see:
33
// http://fsharp.org (F# Open Organization)
34
// http://tryfsharp.org (F# Learning Portal)
35
// http://go.microsoft.com/fwlink/?LinkID=234174 (Visual F# Development Portal)
36
// http://go.microsoft.com/fwlink/?LinkID=124614 (Visual F# Code Gallery)
37
// http://go.microsoft.com/fwlink/?LinkId=235173 (Visual F# Math/Stats Programming)
38
// http://go.microsoft.com/fwlink/?LinkId=235176 (Visual F# Charting)
43
// - Integers and basic functions
47
// - Lists and list processing
50
// - Implementing interfaces
53
// - Recursive functions
59
// - Parallel array programming
61
// - Database access using type providers
62
// - OData access using type providers
65
// ---------------------------------------------------------------
66
// Integers and basic functions
67
// ---------------------------------------------------------------
70
let sampleInteger = 176
72
/// Do some arithmetic starting with the first integer
73
let sampleInteger2 = (sampleInteger/4 + 5 - 7) * 4
75
/// A list of the numbers from 0 to 99
76
let sampleNumbers = [ 0 .. 99 ]
78
/// A list of all tuples containing all the numbers from 0 to 99 and their squares
79
let sampleTableOfSquares = [ for i in 0 .. 99 -> (i, i*i) ]
81
// The next line prints a list that includes tuples, using %A for generic printing
82
printfn "The table of squares from 0 to 99 is:\n%A" sampleTableOfSquares
85
module BasicFunctions =
87
// Use 'let' to define a function that accepts an integer argument and returns an integer.
90
// Parenthesis are optional for function arguments
91
let func1a (x) = x*x + 3
94
/// Apply the function, naming the function return result using 'let'.
95
/// The variable type is inferred from the function return type.
96
let result1 = func1 4573
98
printfn "The result of squaring the integer 4573 and adding 3 is %d" result1
100
// When needed, annotate the type of a parameter name using '(argument:type)'
101
let func2 (x:int) = 2*x*x - x/5 + 3
103
let result2 = func2 (7 + 4)
105
printfn "The result of applying the 1st sample function to (7 + 4) is %d" result2
110
2.0*x*x - x/5.0 + 3.0
112
2.0*x*x + x/5.0 - 37.0
114
let result3 = func3 (6.5 + 4.5)
116
printfn "The result of applying the 2nd sample function to (6.5 + 4.5) is %f" result3
119
// ---------------------------------------------------------------
121
// ---------------------------------------------------------------
124
module SomeBooleanValues =
130
let boolean3 = not boolean1 && (boolean2 || false)
132
printfn "The expression 'not boolean1 && (boolean2 || false)' is %A" boolean3
136
// ---------------------------------------------------------------
138
// ---------------------------------------------------------------
141
module StringManipulation =
143
let string1 = "Hello"
145
let string2 = "world"
147
/// Use @ to create a verbatim string literal
149
let string3 = @"c:\Program Files\"
151
/// Using a triple-quote string literal
152
let string4 = """He said "hello world" after you did"""
154
let helloWorld = string1 + " " + string2 // concatenate the two strings with a space in between
156
printfn "%s" helloWorld
158
/// A string formed by taking the first 7 characters of one of the result strings
159
let substring = helloWorld.[0..6]
161
printfn "%s" substring
165
// ---------------------------------------------------------------
166
// Tuples (ordered sets of values)
167
// ---------------------------------------------------------------
171
/// A simple tuple of integers
172
let tuple1 = (1, 2, 3)
174
/// A function that swaps the order of two values in a tuple.
175
/// QuickInfo shows that the function is inferred to have a generic type.
177
let swapElems (a, b) = (b, a)
179
printfn "The result of swapping (1, 2) is %A" (swapElems (1,2))
181
/// A tuple consisting of an integer, a string, and a double-precision floating point number
182
let tuple2 = (1, "fred", 3.1415)
184
printfn "tuple1: %A tuple2: %A" tuple1 tuple2
188
// ---------------------------------------------------------------
189
// Lists and list processing
190
// ---------------------------------------------------------------
194
let list1 = [ ] /// an empty list
196
let list2 = [ 1; 2; 3 ] /// list of 3 elements
198
let list3 = 42 :: list2 /// a new list with '42' added to the beginning
200
let numberList = [ 1 .. 1000 ] /// list of integers from 1 to 1000
202
/// A list containing all the days of the year
204
[ for month in 1 .. 12 do
205
for day in 1 .. System.DateTime.DaysInMonth(2012, month) do
206
yield System.DateTime(2012, month, day) ]
208
/// A list containing the tuples which are the coordinates of the black squares on a chess board.
213
if (i+j) % 2 = 1 then
216
/// Square the numbers in numberList, using the pipeline operator to pass an argument to List.map
219
|> List.map (fun x -> x*x)
221
/// Computes the sum of the squares of the numbers divisible by 3.
222
let sumOfSquaresUpTo n =
224
|> List.filter (fun x -> x % 3 = 0)
225
|> List.sumBy (fun x -> x * x)
230
// ---------------------------------------------------------------
232
// ---------------------------------------------------------------
235
module DefiningClasses =
236
/// The class's constructor takes two arguments: dx and dy, both of type 'float'.
237
type Vector2D(dx : float, dy : float) =
239
/// The length of the vector, computed when the object is constructed
240
let length = sqrt (dx*dx + dy*dy)
243
// 'this' specifies a name for the object's self identifier
244
// In instance methods, it must appear before the member name.
250
member this.Length = length
252
member this.Scale(k) = Vector2D(k * this.DX, k * this.DY)
255
/// An instance of the Vector2D class
256
let vector1 = Vector2D(3.0, 4.0)
258
/// Get a new scaled vector object, without modifying the original object
259
let vector2 = vector1.Scale(10.0)
261
printfn "Length of vector1: %f Length of vector2: %f" vector1.Length vector2.Length
265
// ---------------------------------------------------------------
267
// ---------------------------------------------------------------
269
module DefiningGenericClasses =
271
type StateTracker<'T>(initialElement: 'T) = // 'T is the type parameter for the class
273
/// Store the states in an array
274
let mutable states = [ initialElement ]
276
/// Add a new element to the list of states
277
member this.UpdateState newState =
278
states <- newState :: states // use the '<-' operator to mutate the value
281
/// Get the entire list of historical states
282
member this.History = states
285
/// Get the latest state
286
member this.Current = states.Head
289
/// An 'int' instance of the state tracker class. Note that the type parameter is inferred.
290
let tracker = StateTracker 10
294
tracker.UpdateState 17
298
// ---------------------------------------------------------------
299
// Implementing interfaces
300
// ---------------------------------------------------------------
302
/// Type that implements IDisposable
306
let file = new System.IO.StreamReader("readme.txt")
308
member this.ReadLine() = file.ReadLine()
310
// this class's implementation of IDisposable members
311
interface System.IDisposable with
313
member this.Dispose() = file.Close()
316
// ---------------------------------------------------------------
318
// ---------------------------------------------------------------
327
let array2 = [| "hello"; "world"; "and"; "hello"; "world"; "again" |]
329
let array3 = [| 1 .. 1000 |]
331
/// An array containing only the words "hello" and "world"
333
let array4 = [| for word in array2 do
334
if word.Contains("l") then
338
/// An array initialized by index and containing the even numbers from 0 to 2000
339
let evenNumbers = Array.init 1001 (fun n -> n * 2)
341
/// sub-array extracted using slicing notation
342
let evenNumbersSlice = evenNumbers.[0..500]
344
for word in array4 do
345
printfn "word: %s" word
347
// modify an array element using the left arrow assignment operator
348
array2.[1] <- "WORLD!"
350
/// Calculates the sum of the lengths of the words that start with 'h'
351
let sumOfLengthsOfWords =
353
|> Array.filter (fun x -> x.StartsWith "h")
354
|> Array.sumBy (fun x -> x.Length)
357
// ---------------------------------------------------------------
359
// ---------------------------------------------------------------
364
// Sequences are evaluated on-demand and are re-evaluated each time they are iterated.
365
// An F# sequence is an instance of a System.Collections.Generic.IEnumerable<'T>,
366
// so Seq functions can be applied to Lists and Arrays as well.
368
/// The empty sequence
371
let seq2 = seq { yield "hello"; yield "world"; yield "and"; yield "hello"; yield "world"; yield "again" }
373
let numbersSeq = seq { 1 .. 1000 }
375
/// another array containing only the words "hello" and "world"
377
seq { for word in seq2 do
378
if word.Contains("l") then
382
let evenNumbers = Seq.init 1001 (fun n -> n * 2)
384
let rnd = System.Random()
386
/// An infinite sequence which is a random walk
387
// Use yield! to return each element of a subsequence, similar to IEnumerable.SelectMany.
389
let rec randomWalk x =
391
yield! randomWalk (x + rnd.NextDouble() - 0.5) }
394
let first100ValuesOfRandomWalk =
400
// ---------------------------------------------------------------
401
// Recursive functions
402
// ---------------------------------------------------------------
405
module RecursiveFunctions =
407
/// Compute the factorial of an integer. Use 'let rec' to define a recursive function
408
let rec factorial n =
409
if n = 0 then 1 else n * factorial (n-1)
411
/// Computes the greatest common factor of two integers.
412
// Since all of the recursive calls are tail calls, the compiler will turn the function into a loop,
413
// which improves performance and reduces memory consumption.
415
let rec greatestCommonFactor a b =
417
elif a < b then greatestCommonFactor a (b - a)
418
else greatestCommonFactor (a - b) b
421
/// Computes the sum of a list of integers using recursion.
425
| y::ys -> y + sumList ys
428
/// Make the function tail recursive, using a helper function with a result accumulator
429
let rec private sumListTailRecHelper accumulator xs =
432
| y::ys -> sumListTailRecHelper (accumulator+y) ys
435
let sumListTailRecursive xs = sumListTailRecHelper 0 xs
439
// ---------------------------------------------------------------
441
// ---------------------------------------------------------------
445
// define a record type
451
let contact1 = { Name = "Alf" ; Phone = "(206) 555-0157" ; Verified = false }
453
// Create a new record that is a copy of contact1,
454
// but has different values for the 'Phone' and 'Verified' fields
456
let contact2 = { contact1 with Phone = "(206) 555-0112"; Verified = true }
459
/// Converts a 'ContactCard' object to a string
461
c.Name + " Phone: " + c.Phone + (if not c.Verified then " (unverified)" else "")
464
// ---------------------------------------------------------------
466
// ---------------------------------------------------------------
470
/// Represents the suit of a playing card
477
/// Represents the rank of a playing card
479
/// Represents the rank of cards 2 .. 10
486
static member GetAllRanks() =
488
for i in 2 .. 10 do yield Value i
493
type Card = { Suit: Suit; Rank: Rank }
495
/// Returns a list representing all the cards in the deck
498
[ for suit in [ Hearts; Diamonds; Clubs; Spades] do
499
for rank in Rank.GetAllRanks() do
500
yield { Suit=suit; Rank=rank } ]
503
/// Converts a 'Card' object to a string
511
| Value n -> string n
516
| Diamonds -> "diamonds"
520
rankString + " of " + suitString
522
let printAllCards() =
523
for card in fullDeck do
524
printfn "%s" (showCard card)
528
// ---------------------------------------------------------------
530
// ---------------------------------------------------------------
533
/// Option values are any kind of value tagged with either 'Some' or 'None'.
534
/// They are used extensively in F# code to represent the cases where many other
535
/// languages would use null references.
537
type Customer = { zipCode : decimal option }
539
/// Abstract class that computes the shipping zone for the customer's zip code,
540
/// given implementations for the 'GetState' and 'GetShippingZone' abstract methods.
544
type ShippingCalculator =
546
abstract GetState : decimal -> string option
548
abstract GetShippingZone : string -> int
550
/// Return the shipping zone corresponding to the customer's ZIP code
551
/// Customer may not yet have a ZIP code or the ZIP code may be invalid
552
member this.CustomerShippingZone(customer : Customer) =
554
customer.zipCode |> Option.bind this.GetState |> Option.map this.GetShippingZone
557
// ---------------------------------------------------------------
559
// ---------------------------------------------------------------
561
module PatternMatching =
563
/// A record for a person's first and last name
570
/// Define a discriminated union of 3 different kinds of employees
573
/// Engineer is just herself
575
/// Manager has list of reports
576
| Manager of Person * list<Employee>
577
/// Executive also has an assistant
578
| Executive of Person * list<Employee> * Employee
580
/// Count everyone underneath the employee in the management hierarchy, including the employee
581
let rec countReports(emp : Employee) =
585
| Manager(id, reports) ->
586
reports |> List.sumBy countReports
587
| Executive(id, reports, assistant) ->
588
(reports |> List.sumBy countReports) + countReports assistant
590
/// Find all managers/executives named "Dave" who do not have any reports
591
let rec findDaveWithOpenPosition(emps : Employee list) =
593
|> List.filter(function
594
| Manager({First = "Dave"}, []) -> true // [] matches the empty list
595
| Executive({First = "Dave"}, [], _) -> true
596
| _ -> false) // '_' is a wildcard pattern that matches anything
597
// this handles the "or else" case
600
// ---------------------------------------------------------------
602
// ---------------------------------------------------------------
604
module UnitsOfMeasure =
605
// Code can be annotated with units of measure when using F# arithmetic over numeric types
607
open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames
612
/// Conversion factor mile to meter: meter is defined in SI.UnitNames
614
static member asMeter = 1600.<meter/mile>
617
let d = 50.<mile> // Distance expressed using imperial units
618
let d' = d * mile.asMeter // Same distance expressed using metric system
620
printfn "%A = %A" d d'
621
// let error = d + d' // Compile error: units of measure do not match
626
// ---------------------------------------------------------------
627
// Parallel array programming
628
// ---------------------------------------------------------------
631
module ParallelArrayProgramming =
633
let oneBigArray = [| 0 .. 100000 |]
635
// do some CPU intensive computation
636
let rec computeSomeFunction x =
638
else computeSomeFunction (x - 1) + computeSomeFunction (x - 2)
640
// Do a parallel map over a large input array
641
let computeResults() = oneBigArray |> Array.Parallel.map (fun x -> computeSomeFunction (x % 20))
643
printfn "Parallel computation results: %A" (computeResults())
647
// ---------------------------------------------------------------
649
// ---------------------------------------------------------------
655
// Create instance of Event object that consists of subscription point (event.Publish) and event trigger (event.Trigger)
656
let simpleEvent = new Event<int>()
659
simpleEvent.Publish.Add(fun x -> printfn "this is handler was added with Publish.Add: %d" x)
662
simpleEvent.Trigger(5)
664
// Create instance of Event that follows standard .NET convention: (sender, EventArgs)
665
let eventForDelegateType = new Event<EventHandler, EventArgs>()
669
eventForDelegateType.Publish.AddHandler(
670
EventHandler(fun _ _ -> printfn "this is handler was added with Publish.AddHandler"))
673
// Trigger event (note that sender argument should be set)
674
eventForDelegateType.Trigger(null, EventArgs.Empty)
678
// ---------------------------------------------------------------
679
// Database access using type providers
680
// ---------------------------------------------------------------
682
module DatabaseAccess =
684
// The easiest way to access a SQL database from F# is to use F# type providers.
685
// Add references to System.Data, System.Data.Linq, and FSharp.Data.TypeProviders.dll.
686
// You can use Server Explorer to build your ConnectionString.
691
#r "System.Data.Linq"
692
#r "FSharp.Data.TypeProviders"
694
open Microsoft.FSharp.Data.TypeProviders
696
type SqlConnection = SqlDataConnection<ConnectionString = @"Data Source=.\sqlexpress;Initial Catalog=tempdb;Integrated Security=True">
697
let db = SqlConnection.GetDataContext()
700
query { for r in db.Table do
706
// You can also use SqlEntityConnection instead of SqlDataConnection, which accesses the database using Entity Framework.
712
// ---------------------------------------------------------------
713
// OData access using type providers
714
// ---------------------------------------------------------------
720
open System.Data.Services.Client
721
open Microsoft.FSharp.Data.TypeProviders
723
// Consume demographics population and income OData service from Azure Marketplace.
724
// For more information, see http://go.microsoft.com/fwlink/?LinkId=239712
726
type Demographics = Microsoft.FSharp.Data.TypeProviders.ODataService<ServiceUri = "https://api.datamarket.azure.com/Esri/KeyUSDemographicsTrial/">
727
let ctx = Demographics.GetDataContext()
729
// Sign up for a Azure Marketplace account at https://datamarket.azure.com/account/info
730
ctx.Credentials <- System.Net.NetworkCredential ("<your liveID>", "<your Azure Marketplace Key>")
733
query { for c in ctx.demog1 do
734
where (c.StateName = "Washington") }
737
printfn "%A - %A" c.GeographyId c.PerCapitaIncome2010.Value
746
module BoilerPlateForForm =
749
// do System.Windows.Forms.Application.Run()