1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
|
// Author:
// Christohper James Halse Rogers <raof@ubuntu.com>
//
// Copyright © 2014 Christopher James Halse Rogers <raof@ubuntu.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// This sample will guide you through elements of the F# language.
//
// *******************************************************************************************************
// To execute the code in F# Interactive, highlight a section of code and press Alt-Enter or right-click
// and select "Execute in Interactive". You can open the F# Interactive Window from the "View" menu.
// *******************************************************************************************************
//
// For more about F#, see:
// http://fsharp.net
//
// For additional templates to use with F#, see the 'Online Templates' in Visual Studio,
// 'New Project' --> 'Online Templates'
//
// For specific F# topics, see:
// http://fsharp.org (F# Open Organization)
// http://tryfsharp.org (F# Learning Portal)
// http://go.microsoft.com/fwlink/?LinkID=234174 (Visual F# Development Portal)
// http://go.microsoft.com/fwlink/?LinkID=124614 (Visual F# Code Gallery)
// http://go.microsoft.com/fwlink/?LinkId=235173 (Visual F# Math/Stats Programming)
// http://go.microsoft.com/fwlink/?LinkId=235176 (Visual F# Charting)
// Contents:
// - Integers and basic functions
// - Booleans
// - Strings
// - Tuples
// - Lists and list processing
// - Classes
// - Generic classes
// - Implementing interfaces
// - Arrays
// - Sequences
// - Recursive functions
// - Record types
// - Union types
// - Option types
// - Pattern matching
// - Units of measure
// - Parallel array programming
// - Using events
// - Database access using type providers
// - OData access using type providers
// ---------------------------------------------------------------
// Integers and basic functions
// ---------------------------------------------------------------
module Integers =
let sampleInteger = 176
/// Do some arithmetic starting with the first integer
let sampleInteger2 = (sampleInteger/4 + 5 - 7) * 4
/// A list of the numbers from 0 to 99
let sampleNumbers = [ 0 .. 99 ]
/// A list of all tuples containing all the numbers from 0 to 99 and their squares
let sampleTableOfSquares = [ for i in 0 .. 99 -> (i, i*i) ]
// The next line prints a list that includes tuples, using %A for generic printing
printfn "The table of squares from 0 to 99 is:\n%A" sampleTableOfSquares
module BasicFunctions =
// Use 'let' to define a function that accepts an integer argument and returns an integer.
let func1 x = x*x + 3
// Parenthesis are optional for function arguments
let func1a (x) = x*x + 3
/// Apply the function, naming the function return result using 'let'.
/// The variable type is inferred from the function return type.
let result1 = func1 4573
printfn "The result of squaring the integer 4573 and adding 3 is %d" result1
// When needed, annotate the type of a parameter name using '(argument:type)'
let func2 (x:int) = 2*x*x - x/5 + 3
let result2 = func2 (7 + 4)
printfn "The result of applying the 1st sample function to (7 + 4) is %d" result2
let func3 x =
if x < 100.0 then
2.0*x*x - x/5.0 + 3.0
else
2.0*x*x + x/5.0 - 37.0
let result3 = func3 (6.5 + 4.5)
printfn "The result of applying the 2nd sample function to (6.5 + 4.5) is %f" result3
// ---------------------------------------------------------------
// Booleans
// ---------------------------------------------------------------
module SomeBooleanValues =
let boolean1 = true
let boolean2 = false
let boolean3 = not boolean1 && (boolean2 || false)
printfn "The expression 'not boolean1 && (boolean2 || false)' is %A" boolean3
// ---------------------------------------------------------------
// Strings
// ---------------------------------------------------------------
module StringManipulation =
let string1 = "Hello"
let string2 = "world"
/// Use @ to create a verbatim string literal
let string3 = @"c:\Program Files\"
/// Using a triple-quote string literal
let string4 = """He said "hello world" after you did"""
let helloWorld = string1 + " " + string2 // concatenate the two strings with a space in between
printfn "%s" helloWorld
/// A string formed by taking the first 7 characters of one of the result strings
let substring = helloWorld.[0..6]
printfn "%s" substring
// ---------------------------------------------------------------
// Tuples (ordered sets of values)
// ---------------------------------------------------------------
module Tuples =
/// A simple tuple of integers
let tuple1 = (1, 2, 3)
/// A function that swaps the order of two values in a tuple.
/// QuickInfo shows that the function is inferred to have a generic type.
let swapElems (a, b) = (b, a)
printfn "The result of swapping (1, 2) is %A" (swapElems (1,2))
/// A tuple consisting of an integer, a string, and a double-precision floating point number
let tuple2 = (1, "fred", 3.1415)
printfn "tuple1: %A tuple2: %A" tuple1 tuple2
// ---------------------------------------------------------------
// Lists and list processing
// ---------------------------------------------------------------
module Lists =
let list1 = [ ] /// an empty list
let list2 = [ 1; 2; 3 ] /// list of 3 elements
let list3 = 42 :: list2 /// a new list with '42' added to the beginning
let numberList = [ 1 .. 1000 ] /// list of integers from 1 to 1000
/// A list containing all the days of the year
let daysList =
[ for month in 1 .. 12 do
for day in 1 .. System.DateTime.DaysInMonth(2012, month) do
yield System.DateTime(2012, month, day) ]
/// A list containing the tuples which are the coordinates of the black squares on a chess board.
let blackSquares =
[ for i in 0 .. 7 do
for j in 0 .. 7 do
if (i+j) % 2 = 1 then
yield (i, j) ]
/// Square the numbers in numberList, using the pipeline operator to pass an argument to List.map
let squares =
numberList
|> List.map (fun x -> x*x)
/// Computes the sum of the squares of the numbers divisible by 3.
let sumOfSquaresUpTo n =
numberList
|> List.filter (fun x -> x % 3 = 0)
|> List.sumBy (fun x -> x * x)
// ---------------------------------------------------------------
// Classes
// ---------------------------------------------------------------
module DefiningClasses =
/// The class's constructor takes two arguments: dx and dy, both of type 'float'.
type Vector2D(dx : float, dy : float) =
/// The length of the vector, computed when the object is constructed
let length = sqrt (dx*dx + dy*dy)
// 'this' specifies a name for the object's self identifier
// In instance methods, it must appear before the member name.
member this.DX = dx
member this.DY = dy
member this.Length = length
member this.Scale(k) = Vector2D(k * this.DX, k * this.DY)
/// An instance of the Vector2D class
let vector1 = Vector2D(3.0, 4.0)
/// Get a new scaled vector object, without modifying the original object
let vector2 = vector1.Scale(10.0)
printfn "Length of vector1: %f Length of vector2: %f" vector1.Length vector2.Length
// ---------------------------------------------------------------
// Generic classes
// ---------------------------------------------------------------
module DefiningGenericClasses =
type StateTracker<'T>(initialElement: 'T) = // 'T is the type parameter for the class
/// Store the states in an array
let mutable states = [ initialElement ]
/// Add a new element to the list of states
member this.UpdateState newState =
states <- newState :: states // use the '<-' operator to mutate the value
/// Get the entire list of historical states
member this.History = states
/// Get the latest state
member this.Current = states.Head
/// An 'int' instance of the state tracker class. Note that the type parameter is inferred.
let tracker = StateTracker 10
// Add a state
tracker.UpdateState 17
// ---------------------------------------------------------------
// Implementing interfaces
// ---------------------------------------------------------------
/// Type that implements IDisposable
type ReadFile() =
let file = new System.IO.StreamReader("readme.txt")
member this.ReadLine() = file.ReadLine()
// this class's implementation of IDisposable members
interface System.IDisposable with
member this.Dispose() = file.Close()
// ---------------------------------------------------------------
// Arrays
// ---------------------------------------------------------------
module Arrays =
/// The empty array
let array1 = [| |]
let array2 = [| "hello"; "world"; "and"; "hello"; "world"; "again" |]
let array3 = [| 1 .. 1000 |]
/// An array containing only the words "hello" and "world"
let array4 = [| for word in array2 do
if word.Contains("l") then
yield word |]
/// An array initialized by index and containing the even numbers from 0 to 2000
let evenNumbers = Array.init 1001 (fun n -> n * 2)
/// sub-array extracted using slicing notation
let evenNumbersSlice = evenNumbers.[0..500]
for word in array4 do
printfn "word: %s" word
// modify an array element using the left arrow assignment operator
array2.[1] <- "WORLD!"
/// Calculates the sum of the lengths of the words that start with 'h'
let sumOfLengthsOfWords =
array2
|> Array.filter (fun x -> x.StartsWith "h")
|> Array.sumBy (fun x -> x.Length)
// ---------------------------------------------------------------
// Sequences
// ---------------------------------------------------------------
module Sequences =
// Sequences are evaluated on-demand and are re-evaluated each time they are iterated.
// An F# sequence is an instance of a System.Collections.Generic.IEnumerable<'T>,
// so Seq functions can be applied to Lists and Arrays as well.
/// The empty sequence
let seq1 = Seq.empty
let seq2 = seq { yield "hello"; yield "world"; yield "and"; yield "hello"; yield "world"; yield "again" }
let numbersSeq = seq { 1 .. 1000 }
/// another array containing only the words "hello" and "world"
let seq3 =
seq { for word in seq2 do
if word.Contains("l") then
yield word }
let evenNumbers = Seq.init 1001 (fun n -> n * 2)
let rnd = System.Random()
/// An infinite sequence which is a random walk
// Use yield! to return each element of a subsequence, similar to IEnumerable.SelectMany.
let rec randomWalk x =
seq { yield x
yield! randomWalk (x + rnd.NextDouble() - 0.5) }
let first100ValuesOfRandomWalk =
randomWalk 5.0
|> Seq.truncate 100
|> Seq.toList
// ---------------------------------------------------------------
// Recursive functions
// ---------------------------------------------------------------
module RecursiveFunctions =
/// Compute the factorial of an integer. Use 'let rec' to define a recursive function
let rec factorial n =
if n = 0 then 1 else n * factorial (n-1)
/// Computes the greatest common factor of two integers.
// Since all of the recursive calls are tail calls, the compiler will turn the function into a loop,
// which improves performance and reduces memory consumption.
let rec greatestCommonFactor a b =
if a = 0 then b
elif a < b then greatestCommonFactor a (b - a)
else greatestCommonFactor (a - b) b
/// Computes the sum of a list of integers using recursion.
let rec sumList xs =
match xs with
| [] -> 0
| y::ys -> y + sumList ys
/// Make the function tail recursive, using a helper function with a result accumulator
let rec private sumListTailRecHelper accumulator xs =
match xs with
| [] -> accumulator
| y::ys -> sumListTailRecHelper (accumulator+y) ys
let sumListTailRecursive xs = sumListTailRecHelper 0 xs
// ---------------------------------------------------------------
// Record types
// ---------------------------------------------------------------
module RecordTypes =
// define a record type
type ContactCard =
{ Name : string
Phone : string
Verified : bool }
let contact1 = { Name = "Alf" ; Phone = "(206) 555-0157" ; Verified = false }
// Create a new record that is a copy of contact1,
// but has different values for the 'Phone' and 'Verified' fields
let contact2 = { contact1 with Phone = "(206) 555-0112"; Verified = true }
/// Converts a 'ContactCard' object to a string
let showCard c =
c.Name + " Phone: " + c.Phone + (if not c.Verified then " (unverified)" else "")
// ---------------------------------------------------------------
// Union types
// ---------------------------------------------------------------
module UnionTypes =
/// Represents the suit of a playing card
type Suit =
| Hearts
| Clubs
| Diamonds
| Spades
/// Represents the rank of a playing card
type Rank =
/// Represents the rank of cards 2 .. 10
| Value of int
| Ace
| King
| Queen
| Jack
static member GetAllRanks() =
[ yield Ace
for i in 2 .. 10 do yield Value i
yield Jack
yield Queen
yield King ]
type Card = { Suit: Suit; Rank: Rank }
/// Returns a list representing all the cards in the deck
let fullDeck =
[ for suit in [ Hearts; Diamonds; Clubs; Spades] do
for rank in Rank.GetAllRanks() do
yield { Suit=suit; Rank=rank } ]
/// Converts a 'Card' object to a string
let showCard c =
let rankString =
match c.Rank with
| Ace -> "Ace"
| King -> "King"
| Queen -> "Queen"
| Jack -> "Jack"
| Value n -> string n
let suitString =
match c.Suit with
| Clubs -> "clubs"
| Diamonds -> "diamonds"
| Spades -> "spades"
| Hearts -> "hearts"
rankString + " of " + suitString
let printAllCards() =
for card in fullDeck do
printfn "%s" (showCard card)
// ---------------------------------------------------------------
// Option types
// ---------------------------------------------------------------
module OptionTypes =
/// Option values are any kind of value tagged with either 'Some' or 'None'.
/// They are used extensively in F# code to represent the cases where many other
/// languages would use null references.
type Customer = { zipCode : decimal option }
/// Abstract class that computes the shipping zone for the customer's zip code,
/// given implementations for the 'GetState' and 'GetShippingZone' abstract methods.
[<AbstractClass>]
type ShippingCalculator =
abstract GetState : decimal -> string option
abstract GetShippingZone : string -> int
/// Return the shipping zone corresponding to the customer's ZIP code
/// Customer may not yet have a ZIP code or the ZIP code may be invalid
member this.CustomerShippingZone(customer : Customer) =
customer.zipCode |> Option.bind this.GetState |> Option.map this.GetShippingZone
// ---------------------------------------------------------------
// Pattern matching
// ---------------------------------------------------------------
module PatternMatching =
/// A record for a person's first and last name
type Person =
{ First : string
Last : string }
/// Define a discriminated union of 3 different kinds of employees
type Employee =
/// Engineer is just herself
| Engineer of Person
/// Manager has list of reports
| Manager of Person * list<Employee>
/// Executive also has an assistant
| Executive of Person * list<Employee> * Employee
/// Count everyone underneath the employee in the management hierarchy, including the employee
let rec countReports(emp : Employee) =
1 + match emp with
| Engineer(id) ->
0
| Manager(id, reports) ->
reports |> List.sumBy countReports
| Executive(id, reports, assistant) ->
(reports |> List.sumBy countReports) + countReports assistant
/// Find all managers/executives named "Dave" who do not have any reports
let rec findDaveWithOpenPosition(emps : Employee list) =
emps
|> List.filter(function
| Manager({First = "Dave"}, []) -> true // [] matches the empty list
| Executive({First = "Dave"}, [], _) -> true
| _ -> false) // '_' is a wildcard pattern that matches anything
// this handles the "or else" case
// ---------------------------------------------------------------
// Units of measure
// ---------------------------------------------------------------
module UnitsOfMeasure =
// Code can be annotated with units of measure when using F# arithmetic over numeric types
open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames
[<Measure>]
type mile =
/// Conversion factor mile to meter: meter is defined in SI.UnitNames
static member asMeter = 1600.<meter/mile>
let d = 50.<mile> // Distance expressed using imperial units
let d' = d * mile.asMeter // Same distance expressed using metric system
printfn "%A = %A" d d'
// let error = d + d' // Compile error: units of measure do not match
// ---------------------------------------------------------------
// Parallel array programming
// ---------------------------------------------------------------
module ParallelArrayProgramming =
let oneBigArray = [| 0 .. 100000 |]
// do some CPU intensive computation
let rec computeSomeFunction x =
if x <= 2 then 1
else computeSomeFunction (x - 1) + computeSomeFunction (x - 2)
// Do a parallel map over a large input array
let computeResults() = oneBigArray |> Array.Parallel.map (fun x -> computeSomeFunction (x % 20))
printfn "Parallel computation results: %A" (computeResults())
// ---------------------------------------------------------------
// Using events
// ---------------------------------------------------------------
module Events =
open System
// Create instance of Event object that consists of subscription point (event.Publish) and event trigger (event.Trigger)
let simpleEvent = new Event<int>()
// Add handler
simpleEvent.Publish.Add(fun x -> printfn "this is handler was added with Publish.Add: %d" x)
// Trigger event
simpleEvent.Trigger(5)
// Create instance of Event that follows standard .NET convention: (sender, EventArgs)
let eventForDelegateType = new Event<EventHandler, EventArgs>()
// Add handler
eventForDelegateType.Publish.AddHandler(
EventHandler(fun _ _ -> printfn "this is handler was added with Publish.AddHandler"))
// Trigger event (note that sender argument should be set)
eventForDelegateType.Trigger(null, EventArgs.Empty)
// ---------------------------------------------------------------
// Database access using type providers
// ---------------------------------------------------------------
module DatabaseAccess =
// The easiest way to access a SQL database from F# is to use F# type providers.
// Add references to System.Data, System.Data.Linq, and FSharp.Data.TypeProviders.dll.
// You can use Server Explorer to build your ConnectionString.
(*
#r "System.Data"
#r "System.Data.Linq"
#r "FSharp.Data.TypeProviders"
open Microsoft.FSharp.Data.TypeProviders
type SqlConnection = SqlDataConnection<ConnectionString = @"Data Source=.\sqlexpress;Initial Catalog=tempdb;Integrated Security=True">
let db = SqlConnection.GetDataContext()
let table =
query { for r in db.Table do
select r }
*)
// You can also use SqlEntityConnection instead of SqlDataConnection, which accesses the database using Entity Framework.
()
// ---------------------------------------------------------------
// OData access using type providers
// ---------------------------------------------------------------
module OData =
(*
open System.Data.Services.Client
open Microsoft.FSharp.Data.TypeProviders
// Consume demographics population and income OData service from Azure Marketplace.
// For more information, see http://go.microsoft.com/fwlink/?LinkId=239712
type Demographics = Microsoft.FSharp.Data.TypeProviders.ODataService<ServiceUri = "https://api.datamarket.azure.com/Esri/KeyUSDemographicsTrial/">
let ctx = Demographics.GetDataContext()
// Sign up for a Azure Marketplace account at https://datamarket.azure.com/account/info
ctx.Credentials <- System.Net.NetworkCredential ("<your liveID>", "<your Azure Marketplace Key>")
let cities =
query { for c in ctx.demog1 do
where (c.StateName = "Washington") }
for c in cities do
printfn "%A - %A" c.GeographyId c.PerCapitaIncome2010.Value
*)
()
#if COMPILED
module BoilerPlateForForm =
[<System.STAThread>]
do ()
// do System.Windows.Forms.Application.Run()
#endif
|