Created
June 26, 2015 00:30
-
-
Save danyaljj/8a1c3e66c6ead4f508fa to your computer and use it in GitHub Desktop.
Fsharp gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Random notes on F# | |
// Daniel Khashabi, June 2014 | |
// References : | |
// http://learnxinyminutes.com/docs/fsharp/ | |
// http://www.fincher.org/tips/Languages/fsharp.shtml | |
// http://fsharpforfunandprofit.com/ | |
// http://en.wikibooks.org/wiki/F_Sharp_Programming | |
open System | |
(*This is a multiline comment | |
And this is another line *) | |
// this is a single line comment | |
// this is a documentation comment | |
// alt + enter runs the code in the interactive | |
// to reset the interactive : | |
// --> right click on the interactive | |
// --> reset interactive | |
// Now : C# directives | |
// Referencing (loading dynamicaly) given DLL file | |
// Can be used only in f# scripts : .fsx or fsscript | |
// #r "file.dll" | |
// Adding the given search pat for the references DLLs | |
// #l "path" | |
// Loading a csharp file | |
// #load "file.fs" | |
// Toggle timing on or off | |
// #time ["on"|"off"] | |
// Exiting from the program : | |
// #quit | |
// set the compiler warning level to light mode --> hash light | |
// #light | |
// pause at the end of the program | |
// Console.ReadKey() |> ignore | |
// or | |
// System.Console.ReadKey(true) | |
let int1 = 5 | |
let float = 4.2 | |
let string1 = "ahsan! " | |
printfn "Integer = %i" int1 | |
printfn "Integer = %f" float | |
printfn "String = %s" string1 | |
// asignments altogether | |
let dog, cat = "bark", "meaw" | |
// all variables are immutable by default | |
// so we can't reassign them | |
// so this is wrong : dog <- "bark2" | |
// also this is not assignment, but just comparison : dog = "bark2" | |
// we define mutable variables this way : | |
let mutable total = 100 | |
printfn "%i" total | |
total <- 200 | |
printfn "%i" total | |
// one can specify the type of the value by adding a suffix to it | |
let a = 0I // --> big num --> arbitrary precision | |
let b = 0u // --> uint32 == uint | |
let c = 2.45f // --> float | |
// one needs to be careful about overflow in F# | |
// it happends quietly | |
let mutable d = System.Int32.MaxValue // note that Int ~ Int32 | |
d <- d + 1 // overflow without any warning or error | |
// the 'unit' type is equivalent to 'null' in java and C# (no object) | |
let x = () | |
// one can assign the values in different bases (why it doesn't work?!) | |
// let hexNumber = 0x | |
// let octalNumber = 0o | |
// let binaryNumber = 0b // or 0B | |
// boolean operators: && || not | |
// one can access elements of an string just like arrays using .[] | |
let aString = "This is a string!" | |
printfn "A character from this string = %c" aString.[0] | |
// or you can just embede the end-of-line and whitespce characters in your assignment | |
let anotherStringWithNewLines = "This | |
has | |
multiple | |
new lines " | |
printfn "Value = %s" anotherStringWithNewLines | |
// we use @ for verbatim strings (in which we don't want to use escape characters ) | |
// just like C# | |
let addresss = @"C:\home\daniel" | |
// if structure | |
// if is an expression and always returns a values | |
// if it doesn't have an else part, or just don't return a value, in that cases it returns 'unit' type | |
let evenOrOdd x = | |
if x % 2 = 0 then | |
printfn "even" | |
else | |
printfn "odd" | |
let getPrice size = | |
let price = | |
if size = "small" then 1.0 | |
elif size = "medium" then 1.5 | |
else 1.75 | |
printfn "the price is %f" price | |
// note that all of the return types must be the same | |
// for example in the avove if the first return type cannot be 1 (instead of 1.0) | |
// looping : can be done explicitely but implicite is preferred | |
for i = 1 to 10 do | |
printfn "i = %d" i | |
for j = 10 downto 5 do | |
printfn "i = %d" j | |
let mutable xx = 0 | |
while xx < 10 do | |
printfn "xx = %d" xx | |
xx <- xx + 1 | |
// lists | |
let twoToFive = [2;3;4;5] | |
let oneToFive = 1::twoToFive // attaching one element | |
let zeroToFive = [0;1] @ twoToFive // concatenating two lists together | |
// an expression can act like a function : | |
let square x = x * x | |
printfn "square 3 is %i" (square 3) | |
let add x y = x + y | |
printfn "add 2 3 is %i" (add 2 3) | |
let nums = [1..4] | |
// using list with a loop | |
let sqr x = x * x | |
let sumOfSquaresI nums = | |
let mutable acc = 0 | |
for x in nums do | |
acc <- acc + sqr x | |
acc | |
printfn "sumOfSquaresI nums = %d" (sumOfSquaresI nums) | |
// A better way using matching | |
let rec sumOfSquaresF nums = | |
match nums with | |
| [] -> 0 | |
| h::t -> sqr h + sumOfSquaresF t | |
printfn "sumOfSquaresF nums = %d" (sumOfSquaresF nums) | |
// Much better way using pipeline | |
let sumOfSquares nums = | |
nums | |
|> Seq.map sqr | |
|> Seq.sum | |
printfn "sumOfSquares nums = %d" (sumOfSquares nums) | |
Console.ReadKey() |> ignore | |
// doing some more interesting stuff | |
let sampleList = [1..15] | |
let evens list = | |
let isEven x = x%2 = 0 | |
List.filter isEven list | |
printfn "The list of evens = %A " (evens sampleList) | |
let sumOfSquaresTo100 = | |
List.sum ( List.map square [1..100]) | |
// this is an implcitely defining a function using 'fun' keyword | |
// and mapping a list element by element with it | |
let sumOfSquaresTo100WithFun = | |
[1..100] |> List.map ( fun x -> x*x ) |> List.sum | |
// pattern matching | |
let simplePatternMatch = | |
let x = "a" | |
match x with | |
| "a" -> printfn "x is a " | |
| "b" -> printfn "x is b " | |
| _ -> printfn "x is something else ! " | |
// "discriminated union"s has structure similar to | |
// matching, using/defining types | |
// Some and None are some predefined types | |
let validValue = Some(99) | |
let invalidValue = None | |
let optionPatternMatch input = | |
match input with | |
| Some i -> printfn "input is %d " i | |
| None -> printfn "input is missing! " | |
optionPatternMatch validValue | |
optionPatternMatch invalidValue | |
printfn "printing int %i, float %f boolean %b" 1 2.0 true | |
printfn "printing %s and something generic %A " "asjih adam! " [1;2;3;4] | |
module FunctionExamples = | |
let add x y = x + y | |
printfn "1 + 2 = %i " (add 1 2 ) | |
let add42 = add 42 | |
printfn "42 + 1 = %i" ( add42 1 ) | |
let add1 = add 1 | |
let add2 = add 2 | |
let add3 = add1 >> add2 // composition : adding functions | |
printfn "3 + 7 = %i" (add3 7) | |
[1..10] |> List.map add3 |> printfn "new list is %A" | |
let add6 = [add1; add2; add3] |> List.reduce(>>) // composition of al functions | |
let d = add6 7 | |
printfn "1+2+3+7 = %i" d | |
// A quick summary : | |
// Lists : immutable | |
// Arrays: mutable | |
// Sequences : immutable, lazy | |
// Sets: immutable | |
// Maps: immutable | |
// list and collection | |
module ListExamples = | |
let list1 = ["a"; "b"] | |
let list2 = "c" :: list1 // :: is prepending | |
let list3 = list1 @ list2 // @ is concatenating ! --> they are different | |
// let list4 = list1 :: list2 --> You can't do this ! | |
let squares = [for i in 1..10 do yield i*i] | |
// prime number : | |
let rec sieve = function | |
| (p::xs) -> p :: sieve [for x in xs do if x % p > 0 then yield x ] | |
| [] -> [] | |
let primes = sieve [2..50] | |
printfn "%A " primes | |
// pattern matching for lists | |
// note the usage of 'first' and 'second' | |
let listMatcher aList = | |
match aList with | |
| [] -> printfn "the list is empty " | |
| [first] -> printfn "the list has one element %A" first | |
| [first; second] -> printfn "list is %A and %A" first second | |
| _ -> printfn "the list has more than two elements " | |
listMatcher [1;2;3;4] | |
listMatcher [1;2] | |
listMatcher [1] | |
listMatcher [] | |
let rec sum aList = | |
match aList with | |
| [] -> 0 | |
| x::xs -> x + sum xs | |
printfn "sum of all elements inside the list = %d" (sum [1..10] ) | |
// standard library functions | |
// generating random numbers | |
open System | |
let ran = new Random() | |
let x = ran.Next() // a big random integer | |
let y = ran.NextDouble() // a random real number in [0, 1] | |
// getting a big integer value | |
let maxVal = System.Int32.MaxValue | |
// maping all elements | |
let add3 x = x + 3 | |
printfn "summing all elements of %A with 3 %A" ([1..10]) ([1..10] |> List.map add3 ) | |
// filter : it filters anything which is labeled zero | |
let even x = x % 2 = 0 | |
printfn "the list of the filtered elements in %A is = %A" [1..10] ([1..10] |> List.filter even ) | |
// arrays | |
// array: represented with : [| |] | |
let fibo = Array.create 20 1 | |
for i in 2..19 do | |
fibo.[i] <- fibo.[i-1] + fibo.[i-2] | |
printfn "%A" fibo | |
for i in 0 .. 19 do | |
printfn "%i " fibo.[i] | |
printfn "%s" " " | |
for i in 19 .. -1 .. 0 do | |
printfn "%i " fibo.[i] | |
printfn "%s" " " | |
// replacing digits with some other digit | |
printfn "Type a number = " | |
let xx:int32 = int32 (Console.ReadLine()) | |
let d1:int32 = int32 (Console.ReadLine()) | |
let d2:int32 = int32 (Console.ReadLine()) | |
let rec swap n d1 d2 = | |
if n = 0 then 0 | |
else | |
if n % 10 = d1 then d2 + 10 * swap(n/10) d1 d2 | |
else n%10 + 10 * swap(n/10) d1 d2 | |
printfn "%i" (swap xx d1 d2) | |
module ArrayExamples = | |
let array1 = [| "a"; "b" |] /// arrays use brackets with bar | |
let first = array1.[0] // indexed access with dot | |
// note about the | |
let arrayMatcher aList = | |
match aList with | |
| [| |] -> printfn "the array is empty ! " | |
| [| first |] -> printfn "has one element %A " first | |
| [| first; second |] -> printfn "the array is %A and %A" first second | |
| _ -> printfn "the array has more than two elements " | |
arrayMatcher [| 1;2;3;4 |] | |
// standard functions just for arrays | |
// Array.iter : runs the same function on each values of the array | |
[| 1..10|] | |
|> Array.map (fun i -> i+3) | |
|> Array.filter (fun i -> i%2 = 0) | |
|> Array.iter (printfn "value is %i") | |
// sequences | |
// unlike lists, sequences ae 'lazy', i.e. their elements are calculated | |
// they are needed. This can be useful when you want to create datastructre | |
// of size very big, since you don't need to use all elemnts, whenever you | |
// want to call one element | |
module SequenceExamples = | |
let seqExample = seq { 1 .. 10 } | |
let seqExample2 = seq { 1 .. 2 .. 10 } | |
let seqExample3 = seq { 10 .. -1 .. 1 } | |
let seqExample4 = seq { for a in 1 .. 10 do yield a, a*a, a*a*a } | |
let seqExample5 = seq {yield 1; yield 2} // --> yield adds one element | |
// like lists, elements, sequences should have | |
// elements of the same type. For example | |
// the following doesn't work | |
//let seq1 = seq {yield 1; yield "a" } | |
// yield adds an element | |
let seqExample6 = seq { | |
yield 1; yield 2 // yield adds one element | |
yield! [5..10] // yield! adds a whole elements | |
yield! seq{ | |
for i in 1..10 do | |
if i%2 = 0 then yield i }} | |
// modules for sequences | |
// convertion to list | |
let convertedToList = seqExample6 |> Seq.toList | |
// appending two sequences | |
let test = Seq.append( seq{1 .. 3} ) ( seq{4 .. 7} ) | |
// choose: filtering and mapping to another sequences | |
// what?! | |
let filterExample = seq { for nm in [Some("Ali"); None; Some("Vali"); Some("Sandali")] | |
|> Seq.choose id -> nm.Length } | |
// distinct : keeps the distinct elements | |
let distincSeq = Seq.distinct ( seq[1;1;1;4;5;5;3;3] ) | |
// determine if en element exists in a sequence | |
let pattern x = x = 2 | |
let existsValue = Seq.exists pattern (seq{3..9}) | |
// filter | |
let filteredSeq = Seq.filter ( fun x -> if x%2=0 then true else false ) (seq{0..9}) | |
// fold: folding with some pattern | |
let sumSeq seq1 = Seq.fold(fun acc elem -> acc + elem ) 0 seq1 | |
Seq.init 10 (fun index -> index * index ) |> sumSeq |> printfn "Sume of the elements is %d" | |
// unfold: creating sequence with some pattern | |
let fib = Seq.unfold( fun( fst, snd ) -> | |
Some( fst + snd, (snd, fst + snd))) (0,1) | |
// take: up to the nth element : | |
let fib10 = fib |> Seq.take 10 |> Seq.toList | |
printf "first 10 fibs are %A" fib10 | |
// nth element : | |
let nThElement = Seq.nth 3 (seq {for n in 2..9 do yield n}) | |
// everything is ummutable by default: except some stuff outside the standard F# like arrays | |
// tuples : | |
// tuples: comma to create tuples : | |
let twoTuple = 1,2 | |
let threeTuple = "a", 2, true | |
// unpacking the tuple | |
let x, y = twoTuple | |
// Records : | |
// have typed fields | |
type Person = {First:string; Last:string} | |
let person1 = {First="John"; Last="Doe"} | |
type ComplexNumber = {Real: float; Imaginary: float} | |
let aComplexNumber = {Real = 1.1; Imaginary = 1.2} | |
let {First=first} = person1 // only sets "John" | |
let {Real=aNum1; Imaginary=aNum2} = aComplexNumber // only sets "John" | |
// discriminated union: | |
// union types aka variants --> only one option is true | |
type Temp = | |
| DegreesC of float | |
| DegreesF of float | |
let temp1 = DegreesF 98.6 | |
let temp2 = DegreesC 37.0 | |
let printTemp = function | |
| DegreesC t -> printfn "%f degC " t | |
| DegreesF t -> printfn "%f degF " t | |
printTemp temp1 | |
printTemp temp2 | |
// I can define the types recursively | |
type Employee = | |
| Worker of Person | |
| Manager of Employee list | |
let jdoe = {First = "John"; Last="Doe"} | |
let worker = Worker jdoe | |
type EmailAddress = | |
| ValidEmailAAddress of string | |
| InvalidEmailAddress of string | |
let trySendEmail email = | |
match email with // pattern matching | |
| ValidEmailAAddress address -> () // send | |
| InvalidEmailAddress address -> () // do not send | |
type CartItem = {ProductionCode : string; Qty: int} | |
type Payment = Payment of float | |
type ActiveCartData = {UnpaidItems : CartItem list} | |
type PaidCartData = {PaidItems: CartItem list; Payment: Payment} | |
type ShoppingCart = | |
| EmptyCart // no data | |
| ActiveCart of ActiveCartData | |
| PaidCart of PaidCartData | |
// Core types | |
// -> Immutable | |
// -> Equaltiy and comparison | |
// -> Serialization | |
type Suit = Club | Diamond | Spade | Heart | |
type Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | |
| Ten | Jack | Queen | King | Ace | |
let hand = [Club, Ace; Heart, Three; Heart, Ace; | |
Spade, Jack; Diamond, Two; Diamond, Ace] | |
// sorting | |
List.sort hand |> printfn "sorted hand is (low to hight, based on the first element) %A" | |
List.max hand |> printfn "max value is %A" | |
List.min hand |> printfn "min value is %A" | |
// active patterns | |
module ActivePatternExamples = | |
// special types of pattern matching for types --> "active patterns" | |
let(|Digit|Letter|Whitespace|Other|) ch = | |
if System.Char.IsDigit(ch) then Digit | |
else if System.Char.IsLetter(ch) then Letter | |
else if System.Char.IsWhiteSpace(ch) then Whitespace | |
else Other | |
let printChar ch = | |
match ch with | |
| Digit -> printfn "%c is a Digit " ch | |
| Letter -> printfn "%c is a Letter " ch | |
| Whitespace -> printfn "%c is a Whitespace " ch | |
| _ -> printfn "%c is something else " ch | |
// print the list | |
['a'; 'b'; '1'; ' '; '-'; 'c'] |> List.iter printChar | |
// Example: fizbuzz | |
let(|MultOf3|_|) i = if i % 3 = 0 then Some MultOf3 else None | |
let(|MultOf5|_|) i = if i % 5 = 0 then Some MultOf5 else None | |
let fizzBuzz i = | |
match i with | |
| MultOf3 & MultOf5 -> printf "FizzBuzz, " | |
| MultOf3 -> printf "Fuzz, " | |
| MultOf5 -> printf "Buzz, " | |
| _ -> printf "%i, " i | |
[1..20 ] |> List.iter fizzBuzz | |
module AlgorithmExamples = | |
// sum of squares | |
let sumOfSquares n = [1..n] |> List.map square |> List.sum | |
// test | |
sumOfSquares 100 |> printfn "sum of squares = %A" | |
// sorting : | |
let rec sort list = | |
match list with | |
|[] -> [] | |
| firstElem::otherElements -> | |
let smallerElements = | |
otherElements | |
|> List.filter (fun e -> e < firstElem) | |
|> sort | |
let largerElements = | |
otherElements | |
|> List.filter (fun e -> e >= firstElem) | |
|> sort | |
List.concat [smallerElements; [firstElem]; largerElements] | |
sort [1; 5; 24; 15; 18; 4; 1; 5] |> printfn "Sorted = %A " | |
// .Net functions | |
module DotNetCompatibilityExampels = | |
// work with the existing libraries | |
let( i1success, i1) = System.UInt32.TryParse("123"); | |
if i1success then printfn "parsed as %i" i1 else printfn "parse failed" | |
// create an objecti that implements IDisposable | |
let makeResource name = | |
{ new System.IDisposable | |
with member this.Dispose() = printfn "%s disposed " name } | |
let useAndDisposeResources = | |
use r1 = makeResource "first resource " | |
printfn "using first resource " | |
for i in [1..3] do | |
let resourceName = sprintf "\tubber resiyrce %d" i | |
use temp = makeResource resourceName | |
printfn "\tdo something with %s" resourceName | |
use r2 = makeResource "second resource " | |
printfn "done! " | |
// object oriented programming | |
// using generic type | |
type IEnumerator<'a> = | |
abstract member Current : 'a | |
abstract MoveNext : unit -> bool | |
// abstract base class with virtual methods | |
[<AbstractClass>] | |
type Shape() = | |
// readonly properties | |
abstract member Width : int with get | |
abstract member Height : int with get | |
// non-virtual member | |
member this.BoundingArea = this.Height * this.Width | |
// virtual method with base implementation | |
abstract member Print : unit -> unit | |
default this.Print () = printfn "I am a shape " | |
(* Why this doesn't work?! :( | |
// concrete ckass --> inherits from base class | |
type Rectangle(x:int, y:int) = | |
inherit Shape() | |
override this.Width() = x | |
override this.Height() = y | |
override this.Print() = printfn "I'm a Rectangle" | |
// test | |
let r = Rectangle(2,3) | |
printfn "The width is %i " r.Width | |
printfn "The area is %i" r.BoundingArea | |
r.Print() | |
*) | |
// extension methods : extending the existing classes with extension metions | |
type System.String with | |
member this.StartsWith = this.StartsWith "A" | |
// events | |
type MyButton() = | |
let clickEvent = new Event<_>() | |
[<CLIEvent>] | |
member this.OnClick = clickEvent.Publish | |
member this.TestEvent(arg) = clickEvent.Trigger(this, arg) | |
let myButton = new MyButton() | |
myButton.OnClick.Add(fun(sender, arg)-> printfn "CLick event with arg = %s" arg ) | |
myButton.TestEvent("Hello World ! ") | |
// some more OO programming | |
// a class for customer | |
type CurtomerNamer(firstName, middleName, lastName ) = | |
member this.FirstName = firstName | |
// with get() = firstName1 | |
// and set(value) = firstName1 <- value | |
member this.MiddleName = middleName | |
member this.LastName = lastName | |
member this.AppendTheNames = this.FirstName + this.LastName | |
member this.DesiredName x = | |
if x = "first" then | |
this.FirstName | |
elif x = "second" then | |
this.LastName | |
else | |
"" | |
let me = new CurtomerNamer("Daniel", "", "Khashabi") | |
printfn "me.AppendTheNames = %s" me.AppendTheNames | |
printfn "me.AppendTheNames = %s" ( me.DesiredName "first" ) | |
// one can specify the types in the constructor | |
(* | |
type CurtomerNamer(firstName:string, middleName:string, lastName:string ) = | |
member this.FirstName = firstName | |
member this.MiddleName = middleName | |
member this.LastName = lastName | |
*) | |
// if we want to send tuple to the constructor we need to annotate it explicitely : | |
type nonTupledConstructor (x:int, y:int) = | |
do printfn "x=%i y=%i" x y | |
type tupledConstructor (tuple:int * int) = | |
let x, y = tuple | |
do printfn "x=%i y=%i" x y | |
let myNTC = new nonTupledConstructor(1,2) | |
let myTC = new tupledConstructor(1,2) | |
System.Console.ReadKey() |> ignore | |
(* | |
[<EntryPoint>] | |
let main(args : string[]) = | |
0 | |
*) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment