Difference between revisions of "FSharp"
(→Pattern matching) |
(→Pattern matching) |
||
Line 121: | Line 121: | ||
printfn "%A" (testNum 3) | printfn "%A" (testNum 3) | ||
− | |||
Active patterns are special functions that can be matched against instead of literal constants. | Active patterns are special functions that can be matched against instead of literal constants. | ||
+ | |||
+ | '''(|''' Starts the definition of an active pattern function. The name should start with an uppercase letter. | ||
+ | '''|)''' Endss the definition of an active pattern function. | ||
+ | '''_''' Wildcard | ||
+ | |||
// Partial active pattern that tests if a number is a power of two | // Partial active pattern that tests if a number is a power of two | ||
let (|IsPow2|_|) n = match n with | let (|IsPow2|_|) n = match n with | ||
Line 135: | Line 139: | ||
| IsPow2 -> "Number is a power of two" | | IsPow2 -> "Number is a power of two" | ||
| _ -> "Number seems unremarkable" | | _ -> "Number seems unremarkable" | ||
+ | |||
+ | Single-case active patterns converts data from one form to another. | ||
+ | Partial active pattern optionally converts data from one from to another and returns the Option type. | ||
+ | Parameterized active patterns take parameters. | ||
==Arrays== | ==Arrays== |
Revision as of 09:47, 25 August 2013
Everything that is indented with space to the same level belongs to the same block. Lines can be be split at any point where it is clear that they are not complete.
- http://www.tryfsharp.org/ - F# tutorials and online compiler
- http://fssnip.net/ - F# source code snippets
- http://tryfs.net/ - Try F# in your web browser
Contents
Values and variables
let a = 2 This is a value and it is constant within the scope of the value. You can safely pass values to other threads, computers or to the other side of the world. Always use values instead of variables when possible.
let mutable b = 2 This is a variable and it can be updated with a new value like in other laguanges b <- b + 1 now b = 3
Types
The compiler will infer types in most cases. let a = 7 - Signed 32 bit integer is the default for numbers without decimal point or any suffix. let b = "Hello" - String let c = 3.1415 - 64 bit floating-point is the default for numbers with a decimal point.
In some cases it is not possible to infer the type or you want to make a point out of it. let (a:uint64) = xPos
A function may not always have something to return, it can then return the Option type. if isByteReady then Some (readByte) else None Use pattern matching to check what was returned from the function.
A tuple is an ordered collection of types. let point = 100,150 The compiler will infer point as two integers. The tuple can be decomposed by let x,y = point
Type Suffix .NET Type Range byte uy System.Byte 0 to 255 sbyte y System.SByte −128 to 127 int16 s System.Int16 −32 768 to 32 767 uint16 us System.UInt16 0 to 65 535 int, int32 System.Int32 −231 to 231 − 1 uint32 u System.UInt32 0 to 232 − 1 int64 L System.Int64 −263 to 263 − 1 uint64 UL System.UInt64 0 to 264 − 1 float System.Double 64 bit floating-point IEEE 64, approximately 15 significant digits. float32 f System.Single 32 bit floating-point IEEE 32, approximately 7 significant digits. decimal M System.Decimal A fixed-precision floating-point type with precisely 28 digits of precision. BigInteger I System.Numerics Arbitrary-sized integers Complex System.Numerics Complex numbers, let (c : Complex) = new Complex(1.0, 2.0)
Functions
Everything is a function and returns a value. The last expression at the point of exit is returned to the caller.
This is a function called max that takes two numbers as input and returns the larger of them. let max a b = if a > b then a else b The if then else is used as a function to return a or b if a > b then a // If a is larger than b then this is the last expression that will be executed so a is returned from the function else b // If b is larger or equal to a then this is the last expression that will be executed so b is returned from the function
If it makes no sense for a function to return a value it should return a value of type unit let a = ()
If the value returned from a function is not required it can be deleted by using the ignore function. like this ignore (max 1 3)
Recursive functions must be defined with the rec keyword. This recursive function counts down from n to 0 let rec count n = printfn "%A" n if n = 0 then () else count (n - 1) If the last statement in the function is the recursive call then the call can be converted to a jump by the compiler. This function does not return anything so the type unity is returned with the keyword ().
Loops
Try to avoid loops when it is simple to do so because most loops require change of state or duplication of count/index values and is a common source of bugs.
let mutable i = 0 while(i < 3) do printfn "Hello World" i <- i + 1
for i = 1 to 10 do printfn "%A" i
// This type of loop is quite safe let arr = ["One";"Two";"Three"] for t in arr do printfn "%A" t //Shorter version for t in ["One";"Two";"Three"] do printfn "%A" t
Pattern matching
Pattern matching should be used in all cases where a simple if then else is not enough. If a match is not found a match failure exception will be raised so make sure there is a match for all possible input. Rules are checked in order from the top down.
| marks the start of a new rule to match. _ is a wildcard and matches anything. -> points to the action to be taken if there is a rule match. when specifies an additional expression that has to be true for a rule to match.
Special case as look-up table let binDigit = function | "0" -> Some 0 | "1" -> Some 1 | _ -> None
General syntax let fTest fn = match fn with | None -> sprintf "File not found" | Some(n) -> sprintf "File '%A' was found" n
Guards are additional expressions following the when keyword that has to be true for a rule to match. let testNum n = match n with | n when n = 0 -> "Number is ZERO" | n when n < 0 -> "Number is negative" | n when (n &&& (n - 1)) = 0 -> "Number is a power of two" | _ -> "Number seems unremarkable" printfn "%A" (testNum 3)
Active patterns are special functions that can be matched against instead of literal constants.
(| Starts the definition of an active pattern function. The name should start with an uppercase letter. |) Endss the definition of an active pattern function. _ Wildcard
// Partial active pattern that tests if a number is a power of two let (|IsPow2|_|) n = match n with | 0 -> None | n when (n &&& (n - 1)) = 0 -> Some () | _ -> None let testNum n = match n with | n when n = 0 -> "Number is ZERO" | n when n < 0 -> "Number is negative" | IsPow2 -> "Number is a power of two" | _ -> "Number seems unremarkable" Single-case active patterns converts data from one form to another. Partial active pattern optionally converts data from one from to another and returns the Option type. Parameterized active patterns take parameters.
Arrays
The arrays are zero-based where the first element has index 0. Arrays are fast as long as they are fixed length.
; is the delimiter between items. .. is used to specify a range of numbers. [| start of array |] end of array
Creating single-dimensional arrays Resulting array let a = [||] [||] (Empty array) let a = [|"a";"b";"c"|] [|"a";"b";"c"|] let a = [|1..6|] [|1; 2; 3; 4; 5; 6|] (Counting from 1 to 6) let a = [|1..2..6|] [|1; 3; 5|] (Counting from 1 to 6 increment 2) let a : int [] = Array.zeroCreate 3 [|0; 0; 0|] let a = Array.init 5 (fun i -> i * i) [|0; 1; 4; 9; 16|] let a = [|for i = 1 to 3 do yield i * 3|] [|3; 6; 9|]
Access the third item in the array. let b = a.[2]
Slicing arrays by index range let a = [|"a";"b";"c";"d"|] Resulting array let c = a.[1..2] [|"b";"c"|] let d = a.[..2] [|"a";"b";"c"|] let e = a.[1..] [|"b";"c";"d"|]
Lists
Lists are fast and memory efficient when items are added or removed from the start. Random access and operations at the end of the list are slow.
Creating lists works the same as creating arrays. let a = [] let a = [1;2;3] ...
Adding one element to the front of a list using the Cons operator. This is extremely efficient since the new element just links to the original list. If you need to add to the end of the list you can reverse the list after it is completed. let a = [1;2;3] let b = 0 :: a // b = [0;1,2,3]
Joining two lists using the Append operator. let a = [1;2;3] let b = [4;5;6] let c = a @ b // c = [1;2;3;4;5;6] Always use the cons operator when possible.
Sequences
A sequence is an ordered sequence of items like a list. The main difference is that a sequence does not have to exist in memory, it can be computed on the fly and can be infinite.
Creating sequences works mostly the same as creating lists. The sequence expression can be recursive using the yield! operator. let a = seq{1..3} // a = seq [1; 2; 3] ...
Processing data in a functional way
The easiest method is to run a function on every item of a collection of items to create a new collection.
let a = [1..5] // The list to process let mul2 n = n * 2 // Function that will be run on each item in the list let b = List.map mul2 a // Mapping the input list a through the function mul2 to the output list b // The operation is // 1 -> mul2 -> 2 // 2 -> mul2 -> 4 // 3 -> mul2 -> 6 // 4 -> mul2 -> 8 // 5 -> mul2 -> 10
Using the fun keyword we can create an anonymous function in place This is helpful if the function is simple and only is required once. let b = List.map (fun n -> n * 2) a
The other method is to use a recursive function.
let mul2 l = let rec loop listIn listOut = match listIn with | [] -> listOut | h::t -> loop t (h * 2 :: listOut) List.rev (loop l []) mul2 [1;2;3;4;5] // Gives [2;4;6;8;10] Using a recursive function inside a normal function makes it easy to hide implementation details but is not required. | [] matches an empty list and -> listOut returns the result. | h::t will match any list and decompose it into head and tail where head is the first item of the list and tail is the remainder. -> loop t (h * 2 :: listOut) will call loop with the tail as the new listIn and append h*2 to the front of listOut as the new listOut. loop l [] starts the recursive function with the input to mul2 as listIn and an empty list as listOut. The output list is built in the reverse order and List.rev will reverse the list just before it is returned from mul2.
Parallel computing
Functions run in parallel may start and complete in any order.
Array.Parallel Parallel.For
Printing text
printf Print formatted text printfn Print formatted text + newline sprintf Print formatted string
Format specifiers %d, %i Integer %x, %o Integer in Hex or Octal %s String %f Floating-point %c Character %b boolean %O Object %A Anything, this is useful when you are writing a quick script or you are making changes to types. Using the exact format specifier instead of %A will help with type inference in complex programs. let n = 5 Result printfn "The number is %i " n The number is 5
Prints any type in a simple way. let print t = printfn "%A" t
Prints any type and passes it on so you can insert the function in a pipeline to show what passes through. let show t = print t t