- F# 基础教程
- F# - 主页
- F# - 概述
- F# - 环境设置
- F# - 程序结构
- F# - 基本语法
- F# - 数据类型
- F# - 变量
- F# - 运算符
- F# - 决策
- F# - 循环
- F# - 函数
- F# - 字符串
- F# - 选项
- F# - 元组
- F# - 记录
- F# - 列表
- F# - 序列
- F# - 集
- F# - 地图
- F# - 受歧视的工会
- F# - 可变数据
- F# - 数组
- F# - 可变列表
- F# - 可变字典
- F# - 基本 I/O
- F# - 泛型
- F# - 代表
- F# - 枚举
- F# - 模式匹配
- F# - 异常处理
- F# - 类
- F# - 结构
- F# - 运算符重载
- F# - 继承
- F# - 接口
- F# - 事件
- F# - 模块
- F# - 命名空间
- F# 有用资源
- F# - 快速指南
- F# - 有用的资源
- F# - 讨论
F# - Quick Guide
F# - Overview
F# is a functional programming language. To understand F# constructs, you need to read a couple of lines about the programming paradigm named Functional Programming.
Functional programming treats computer programs as mathematical functions. In functional programming, the focus would be on constants and functions, instead of variables and states. Because functions and constants are things that don’t change.
In functional programming, you will write modular programs, i.e., the programs would consist of functions that will take other functions as input.
Programs written in functional programming language tend to be concise.
About F#
Following are the basic information about F# −
- It was developed in 2005 at Microsoft Research.
- It is a part of Microsoft’s family of .Net language.
- It is a functional programming language.
- It is based on the functional programming language OCaml.
Features of F#
It is .Net implementation of OCaml.
It compiles .Net CLI (Common Language Interface) byte code or MSIL (Microsoft Intermediate Language) that runs on CLR (Common Language Runtime).
It provides type inference.
It provides rich pattern matching constructs.
It has interactive scripting and debugging capabilities.
It allows writing higher order functions.
It provides well developed object model.
Use of F#
F# is normally used in the following areas −
- Making scientific model
- Mathematical problem solving
- Artificial intelligence research work
- Financial modelling
- Graphic design
- CPU design
- Compiler programming
- Telecommunications
It is also used in CRUD apps, web pages, GUI games and other general purpose programs.
F# - Environment Setup
The tools required for F# programming are discussed in this chapter.
Integrated Development Environment(IDE) for F#
Microsoft provides Visual Studio 2013 for F# programming.
The free Visual Studio 2013 Community Edition is available from Microsoft’s official website. Visual Studio 2013 Community and above comes with the Visual F# Tools. Installation details available at Asp.net Tutorial .The Visual F# Tools include the command-line compiler (fsc.exe) and F# Interactive (fsi.exe).
Using these tools, you can write all kinds of F# programs from simple command-line applications to more complex applications. You can also write F# source code files using a basic text editor, like Notepad, and compile the code into assemblies using the command-line compiler.
You can download it from Microsoft Visual Studio. It gets automatically installed in your machine.
Writing F# Programs On Links
Please visit the F# official website for the latest instructions on getting the tools as a Debian package or compiling them directly from the source − https://fsharp.org/use/linux/.
F# - Program Structure
F# is a Functional Programming language.
In F#, functions work like data types. You can declare and use a function in the same way like any other variable.
In general, an F# application does not have any specific entry point. The compiler executes all top-level statements in the file from top to bottom.
However, to follow procedural programming style, many applications keep a single top level statement that calls the main loop.
The following code shows a simple F# program −
open System (* This is a multi-line comment *) // This is a single-line comment let sign num = if num > 0 then "positive" elif num < 0 then "negative" else "zero" let main() = Console.WriteLine("sign 5: {0}", (sign 5)) main()
When you compile and execute the program, it yields the following output −
sign 5: positive
Please note that −
An F# code file might begin with a number of open statements that is used to import namespaces.
The body of the files includes other functions that implement the business logic of the application.
The main loop contains the top executable statements.
F# - Basic Syntax
You have seen the basic structure of an F# program, so it will be easy to understand other basic building blocks of the F# programming language.
Tokens in F#
An F# program consists of various tokens. A token could be a keyword, an identifier, a constant, a string literal, or a symbol. We can categorize F# tokens into two types −
- Keywords
- Symbol and Operators
F# Keywords
The following table shows the keywords and brief descriptions of the keywords. We will discuss the use of these keywords in subsequent chapters.
Keyword | Description |
---|---|
abstract | Indicates a method that either has no implementation in the type in which it is declared or that is virtual and has a default implementation. |
and | Used in mutually recursive bindings, in property declarations, and with multiple constraints on generic parameters. |
as | Used to give the current class object an object name. Also used to give a name to a whole pattern within a pattern match. |
assert | Used to verify code during debugging. |
base | Used as the name of the base class object. |
begin | In verbose syntax, indicates the start of a code block. |
class | In verbose syntax, indicates the start of a class definition. |
default | Indicates an implementation of an abstract method; used together with an abstract method declaration to create a virtual method. |
delegate | Used to declare a delegate. |
do | Used in looping constructs or to execute imperative code. |
done | In verbose syntax, indicates the end of a block of code in a looping expression. |
downcast | Used to convert to a type that is lower in the inheritance chain. |
downto | In a for expression, used when counting in reverse. |
elif | Used in conditional branching. A short form of else if. |
else | Used in conditional branching. |
end | In type definitions and type extensions, indicates the end of a section of member definitions. In verbose syntax, used to specify the end of a code block that starts with the begin keyword. |
exception | Used to declare an exception type. |
extern | Indicates that a declared program element is defined in another binary or assembly. |
false | Used as a Boolean literal. |
finally | Used together with try to introduce a block of code that executes regardless of whether an exception occurs. |
for | Used in looping constructs. |
fun | Used in lambda expressions, also known as anonymous functions. |
function | Used as a shorter alternative to the fun keyword and a match expression in a lambda expression that has pattern matching on a single argument. |
global | Used to reference the top-level .NET namespace. |
if | Used in conditional branching constructs. |
in | Used for sequence expressions and, in verbose syntax, to separate expressions from bindings. |
inherit | Used to specify a base class or base interface. |
inline | Used to indicate a function that should be integrated directly into the caller's code. |
interface | Used to declare and implement interfaces. |
internal | Used to specify that a member is visible inside an assembly but not outside it. |
lazy | Used to specify a computation that is to be performed only when a result is needed. |
let | Used to associate, or bind, a name to a value or function. |
let! | Used in asynchronous workflows to bind a name to the result of an asynchronous computation, or, in other computation expressions, used to bind a name to a result, which is of the computation type. |
match | Used to branch by comparing a value to a pattern. |
member | Used to declare a property or method in an object type. |
module | Used to associate a name with a group of related types, values, and functions, to logically separate it from other code. |
mutable | Used to declare a variable, that is, a value that can be changed. |
namespace | Used to associate a name with a group of related types and modules, to logically separate it from other code. |
new | Used to declare, define, or invoke a constructor that creates or that can create an object. Also used in generic parameter constraints to indicate that a type must have a certain constructor. |
not | Not actually a keyword. However, not struct in combination is used as a generic parameter constraint. |
null | Indicates the absence of an object. Also used in generic parameter constraints. |
of | Used in discriminated unions to indicate the type of categories of values, and in delegate and exception declarations. |
open | Used to make the contents of a namespace or module available without qualification. |
or | Used with Boolean conditions as a Boolean or operator. Equivalent to ||. Also used in member constraints. |
override | Used to implement a version of an abstract or virtual method that differs from the base version. |
private | Restricts access to a member to code in the same type or module. |
public | Allows access to a member from outside the type. |
rec | Used to indicate that a function is recursive. |
return | Used to indicate a value to provide as the result of a computation expression. |
return! | Used to indicate a computation expression that, when evaluated, provides the result of the containing computation expression. |
select | Used in query expressions to specify what fields or columns to extract. Note that this is a contextual keyword, which means that it is not actually a reserved word and it only acts like a keyword in appropriate context. |
static | Used to indicate a method or property that can be called without an instance of a type, or a value member that is shared among all instances of a type. |
struct | Used to declare a structure type. Also used in generic parameter constraints. Used for OCaml compatibility in module definitions. |
then | Used in conditional expressions. Also used to perform side effects after object construction. |
to | Used in for loops to indicate a range. |
true | Used as a Boolean literal. |
try | Used to introduce a block of code that might generate an exception. Used together with with or finally. |
type | Used to declare a class, record, structure, discriminated union, enumeration type, unit of measure, or type abbreviation. |
upcast | Used to convert to a type that is higher in the inheritance chain. |
use | Used instead of let for values that require Dispose to be called to free resources. |
use! | Used instead of let! in asynchronous workflows and other computation expressions for values that require Dispose to be called to free resources. |
val | Used in a signature to indicate a value, or in a type to declare a member, in limited situations. |
void | Indicates the .NET void type. Used when interoperating with other .NET languages. |
when | Used for Boolean conditions (when guards) on pattern matches and to introduce a constraint clause for a generic type parameter. |
while | Introduces a looping construct. |
with | Used together with the match keyword in pattern matching expressions. Also used in object expressions, record copying expressions, and type extensions to introduce member definitions, and to introduce exception handlers. |
yield | Used in a sequence expression to produce a value for a sequence. |
yield! | Used in a computation expression to append the result of a given computation expression to a collection of results for the containing computation expression. |
Some reserved keywords came from the OCaml language −
asr | land | lor | lsl | lsr | lxor | mod | sig |
Some other reserved keywords are kept for future expansion of F#.
atomic | break | checked | component | const | constraint | constructor |
continue | eager | event | external | fixed | functor | include |
method | mixin | object | parallel | process | protected | pure |
sealed | tailcall | trait | virtual | volatile |
Comments in F#
F# provides two types of comments −
- One line comment starts with // symbol.
- Multi line comment starts with (* and ends with *).
A Basic Program and Application Entry Point in F#
Generally, you don’t have any explicit entry point for F# programs. When you compile an F# application, the last file provided to the compiler becomes the entry point and all top level statements in that file are executed from top to bottom.
A well-written program should have a single top-level statement that would call the main loop of the program.
A very minimalistic F# program that would display ‘Hello World’ on the screen −
(* This is a comment *) (* Sample Hello World program using F# *) printfn "Hello World!"
When you compile and execute the program, it yields the following output −
Hello World!
F# - Data Types
The data types in F# can be classified as follows −
- Integral types
- Floating point types
- Text types
- Other types
Integral Data Type
The following table provides the integral data types of F#. These are basically integer data types.
F# Type | Size | Range | Example | Remarks |
---|---|---|---|---|
sbyte | 1 byte | -128 to 127 | 42y -11y |
8-bit signed integer |
byte | 1 byte | 0 to 255 | 42uy 200uy |
8-bit unsigned integer |
int16 | 2 bytes | -32768 to 32767 | 42s -11s |
16-bit signed integer |
uint16 | 2 bytes | 0 to 65,535 | 42us 200us |
16-bit unsigned integer |
int/int32 | 4 bytes | -2,147,483,648 to 2,147,483,647 | 42 -11 |
32-bit signed integer |
uint32 | 4 bytes | 0 to 4,294,967,295 | 42u 200u |
32-bit unsigned integer |
int64 | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 42L -11L |
64-bit signed integer |
uint64 | 8 bytes | 0 to 18,446,744,073,709,551,615 | 42UL 200UL |
64-bit unsigned integer |
bigint | At least 4 bytes | any integer | 42I 1499999 9999999 9999999 9999999 9999I |
arbitrary precision integer |
Example
(* single byte integer *) let x = 268.97f let y = 312.58f let z = x + y printfn "x: %f" x printfn "y: %f" y printfn "z: %f" z (* unsigned 8-bit natural number *) let p = 2uy let q = 4uy let r = p + q printfn "p: %i" p printfn "q: %i" q printfn "r: %i" r (* signed 16-bit integer *) let a = 12s let b = 24s let c = a + b printfn "a: %i" a printfn "b: %i" b printfn "c: %i" c (* signed 32-bit integer *) let d = 212l let e = 504l let f = d + e printfn "d: %i" d printfn "e: %i" e printfn "f: %i" f
When you compile and execute the program, it yields the following output −
x: 1 y: 2 z: 3 p: 2 q: 4 r: 6 a: 12 b: 24 c: 36 d: 212 e: 504 f: 716
Floating Point Data Types
The following table provides the floating point data types of F#.
F# Type | Size | Range | Example | Remarks |
---|---|---|---|---|
float32 | 4 bytes | ±1.5e-45 to ±3.4e38 | 42.0F -11.0F |
32-bit signed floating point number (7 significant digits) |
float | 8 bytes | ±5.0e-324 to ±1.7e308 | 42.0 -11.0 |
64-bit signed floating point number (15-16 significant digits) |
decimal | 16 bytes | ±1.0e-28 to ±7.9e28 | 42.0M -11.0M |
128-bit signed floating point number (28-29 significant digits) |
BigRational | At least 4 bytes | Any rational number. | 42N -11N |
Arbitrary precision rational number. Using this type requires a reference to FSharp.PowerPack.dll. |
Example
(* 32-bit signed floating point number *) (* 7 significant digits *) let d = 212.098f let e = 504.768f let f = d + e printfn "d: %f" d printfn "e: %f" e printfn "f: %f" f (* 64-bit signed floating point number *) (* 15-16 significant digits *) let x = 21290.098 let y = 50446.768 let z = x + y printfn "x: %g" x printfn "y: %g" y printfn "z: %g" z
When you compile and execute the program, it yields the following output −
d: 212.098000 e: 504.768000 f: 716.866000 x: 21290.1 y: 50446.8 z: 71736.9
Text Data Types
The following table provides the text data types of F#.
F# Type | Size | Range | Example | Remarks |
---|---|---|---|---|
char | 2 bytes | U+0000 to U+ffff | 'x' '\t' |
Single unicode characters |
string | 20 + (2 * string's length) bytes | 0 to about 2 billion characters | "Hello" "World" |
Unicode text |
Example
let choice = 'y' let name = "Zara Ali" let org = "Tutorials Point" printfn "Choice: %c" choice printfn "Name: %s" name printfn "Organisation: %s" org
When you compile and execute the program, it yields the following output −
Choice: y Name: Zara Ali Organisation: Tutorials Point
Other Data Types
The following table provides some other data types of F#.
F# Type | Size | Range | Example | Remarks |
---|---|---|---|---|
bool | 1 byte | Only two possible values, true or false | true false |
Stores boolean values |
Example
let trueVal = true let falseVal = false printfn "True Value: %b" (trueVal) printfn "False Value: %b" (falseVal)
When you compile and execute the program, it yields the following output −
True Value: true False Value: false
F# - Variables
A variable is a name given to a storage area that our programs can manipulate. Each variable has a specific type, which determines the size and layout of the variable's memory; the range of values that can be stored within that memory; and the set of operations that can be applied to the variable.
Variable Declaration in F#
The let keyword is used for variable declaration −
For example,
let x = 10
It declares a variable x and assigns the value 10 to it.
You can also assign an expression to a variable −
let x = 10 let y = 20 let z = x + y
The following example illustrates the concept −
Example
let x = 10 let y = 20 let z = x + y printfn "x: %i" x printfn "y: %i" y printfn "z: %i" z
When you compile and execute the program, it yields the following output −
x: 10 y: 20 z: 30
Variables in F# are immutable, which means once a variable is bound to a value, it can’t be changed. They are actually compiled as static read-only properties.
The following example demonstrates this.
Example
let x = 10 let y = 20 let z = x + y printfn "x: %i" x printfn "y: %i" y printfn "z: %i" z let x = 15 let y = 20 let z = x + y printfn "x: %i" x printfn "y: %i" y printfn "z: %i" z
When you compile and execute the program, it shows the following error message −
Duplicate definition of value 'x' Duplicate definition of value 'Y' Duplicate definition of value 'Z'
Variable Definition With Type Declaration
A variable definition tells the compiler where and how much storage for the variable should be created. A variable definition may specify a data type and contains a list of one or more variables of that type as shown in the following example.
Example
let x:int32 = 10 let y:int32 = 20 let z:int32 = x + y printfn "x: %d" x printfn "y: %d" y printfn "z: %d" z let p:float = 15.99 let q:float = 20.78 let r:float = p + q printfn "p: %g" p printfn "q: %g" q printfn "r: %g" r
When you compile and execute the program, it shows the following error message −
x: 10 y: 20 z: 30 p: 15.99 q: 20.78 r: 36.77
Mutable Variables
At times you need to change the values stored in a variable. To specify that there could be a change in the value of a declared and assigned variable, in later part of a program, F# provides the mutable keyword. You can declare and assign mutable variables using this keyword, whose values you will change.
The mutable keyword allows you to declare and assign values in a mutable variable.
You can assign some initial value to a mutable variable using the let keyword. However, to assign new subsequent value to it, you need to use the ← operator.
For example,
let mutable x = 10 x ← 15
The following example will clear the concept −
Example
let mutable x = 10 let y = 20 let mutable z = x + y printfn "Original Values:" printfn "x: %i" x printfn "y: %i" y printfn "z: %i" z printfn "Let us change the value of x" printfn "Value of z will change too." x <- 15 z <- x + y printfn "New Values:" printfn "x: %i" x printfn "y: %i" y printfn "z: %i" z
When you compile and execute the program, it yields the following output −
Original Values: x: 10 y: 20 z: 30 Let us change the value of x Value of z will change too. New Values: x: 15 y: 20 z: 35
F# - Operators
An operator is a symbol that tells the compiler to perform specific mathematical or logical manipulations. F# is rich in built-in operators and provides the following types of operators −
- Arithmetic Operators
- Comparison Operators
- Boolean Operators
- Bitwise Operators
Arithmetic Operators
The following table shows all the arithmetic operators supported by F# language. Assume variable A holds 10 and variable B holds 20 then −
Operator | Description | Example |
---|---|---|
+ | Adds two operands | A + B will give 30 |
- | Subtracts second operand from the first | A - B will give -10 |
* | Multiplies both operands | A * B will give 200 |
/ | Divides numerator by de-numerator | B / A will give 2 |
% | Modulus Operator and remainder of after an integer division | B % A will give 0 |
** | Exponentiation Operator, raises an operand to the power of another | B**A will give 2010 |
Comparison Operators
The following table shows all the comparison operators supported by F# language. These binary comparison operators are available for integral and floating-point types. These operators return values of type bool.
Assume variable A holds 10 and variable B holds 20, then −
Operator | Description | Example |
---|---|---|
= | Checks if the values of two operands are equal or not, if yes then condition becomes true. | (A == B) is not true. |
<> | Checks if the values of two operands are equal or not, if values are not equal then condition becomes true. | (A <> B) is true. |
> | Checks if the value of left operand is greater than the value of right operand, if yes then condition becomes true. | (A > B) is not true. |
< | Checks if the value of left operand is less than the value of right operand, if yes then condition becomes true. | (A < B) is true. |
>= | Checks if the value of left operand is greater than or equal to the value of right operand, if yes then condition becomes true. | (A >= B) is not true. |
<= | Checks if the value of left operand is less than or equal to the value of right operand, if yes then condition becomes true. | (A <= B) is true. |
Boolean Operators
The following table shows all the Boolean operators supported by F# language. Assume variable A holds true and variable B holds false, then −
Operator | Description | Example |
---|---|---|
&& | Called Boolean AND operator. If both the operands are non-zero, then condition becomes true. | (A && B) is false. |
|| | Called Boolean OR Operator. If any of the two operands is non-zero, then condition becomes true. | (A || B) is true. |
not | Called Boolean NOT Operator. Use to reverses the logical state of its operand. If a condition is true then Logical NOT operator will make false. | not (A && B) is true. |
Bitwise Operators
Bitwise operators work on bits and perform bit-by-bit operation. The truth tables for &&& (bitwise AND), ||| (bitwise OR), and ^^^ (bitwise exclusive OR) are as follows −
p | q | p &&& q | p ||| q | p ^^^ q |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
Assume if A = 60; and B = 13; now in binary format they will be as follows −
A = 0011 1100
B = 0000 1101
-----------------A&&&B = 0000 1100
A|||B = 0011 1101
A^^^B = 0011 0001
~~~A = 1100 0011
The Bitwise operators supported by F# language are listed in the following table. Assume variable A holds 60 and variable B holds 13, then −
Operator | Description | Example |
---|---|---|
&&& | Binary AND Operator copies a bit to the result if it exists in both operands. | (A &&& B) will give 12, which is 0000 1100 |
||| | Binary OR Operator copies a bit if it exists in either operand. | (A ||| B) will give 61, which is 0011 1101 |
^^^ | Binary XOR Operator copies the bit if it is set in one operand but not both. | (A ^^^ B) will give 49, which is 0011 0001 |
~~~ | Binary Ones Complement Operator is unary and has the effect of 'flipping' bits. | (~~~A) will give -61, which is 1100 0011 in 2's complement form. |
<<< | Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand. | A <<< 2 will give 240 which is 1111 0000 |
>>> | Binary Right Shift Operator. The left operands value is moved right by the number of bits specified by the right operand. | A >>> 2 will give 15 which is 0000 1111 |
Operators Precedence
The following table shows the order of precedence of operators and other expression keywords in the F# language, from lowest precedence to the highest precedence.
Operator | Associativity |
---|---|
as | Right |
when | Right |
| (pipe) | Left |
; | Right |
let | Non associative |
function, fun, match, try | Non associative |
if | Non associative |
→ | Right |
:= | Right |
, | Non associative |
or, || | Left |
&, && | Left |
< op, >op, =, |op, &op | Left |
&&& , |||, ^^^, ~~~, <<<, >>> | Left |
^ op | Right |
:: | Right |
:?>, :? | Non associative |
- op, +op, (binary) | Left |
* op, /op, %op | Left |
** op | Right |
f x (function application) | Left |
| (pattern match) | Right |
prefix operators (+op, -op, %, %%, &, &&, !op, ~op) | Left |
. | Left |
f(x) | Left |
f<types> | Left |
F# - Decision Making
Decision making structures require that the programmer specify one or more conditions to be evaluated or tested by the program. It should be along with a statement or statements to be executed if the condition is determined to be true, and optionally, other statements to be executed if the condition is determined to be false.
Following is the general form of a typical decision making structure found in most of the programming languages −
F# programming language provides the following types of decision making statements.
Statement | Description |
---|---|
if /then statement | An if/then statement consists of a Boolean expression followed by one or more statements. |
if/then/ else statement | An if/then statement can be followed by an optional else statement, which executes when the Boolean expression is false. |
if/then/elif/else statement | An if/then/elif/else statement allows you to have multiple else branches. |
nested if statements | You can use one if or else if statement inside another if or else if statement(s). |
F# - Loops
Programming languages provide various control structures that allow for more complicated execution paths.
A loop statement allows us to execute a statement or group of statements multiple times and following is the general form of a loop statement in most of the programming languages −
F# provides the following types of loops to handle the looping requirements.
Loop Type | Description |
---|---|
for… to and for… downto expressions | The for...to expression is used to iterate in a loop over a range of values of a loop variable. The for… downto expression reduces the value of loop variable. |
for … in expression | This form of for loop is used to iterate over collections of items i.e., loops over collections and sequences |
While…do loop | Repeats a statement or group of statements while a given condition is true. It tests the condition before executing the loop body. |
nested loops | You can use one or more loop inside any other for or while loop. |
F# - Functions
In F#, functions work like data types. You can declare and use a function in the same way like any other variable.
Since functions can be used like any other variables, you can −
- Create a function, with a name and associate that name with a type.
- Assign it a value.
- Perform some calculation on that value.
- Pass it as a parameter to another function or sub-routine.
- Return a function as the result of another function.
Defining a Function
Functions are defined by using the let keyword. A function definition has the following syntax −
let [inline] function-name parameter-list [ : return-type ] = function-body
Where,
function-name is an identifier that represents the function.
parameter-list gives the list of parameters separated by spaces. You can also specify an explicit type for each parameter and if not specified compiler tends to deduce it from the function body (like variables).
function-body consists of an expression, or a compound expression consisting of a number of expressions. The final expression in the function body is the return value.
return-type is a colon followed by a type and is optional. If the return type is not specified, then the compiler determines it from the final expression in the function body.
Parameters of a Function
You list the names of parameters right after the function name. You can specify the type of a parameter. The type of the parameter should follow the name of the parameter separated by a colon.
If no parameter type is specified, it is inferred by the compiler.
For example −
let doubleIt (x : int) = 2 * x
Calling a Function
A function is called by specifying the function name followed by a space and then any arguments separated by spaces.
For example −
let vol = cylinderVolume 3.0 5.0
The following programs illustrate the concepts.
Example 1
The following program calculates the volume of a cylinder when the radius and length are given as parameters
// the function calculates the volume of // a cylinder with radius and length as parameters let cylinderVolume radius length : float = // function body let pi = 3.14159 length * pi * radius * radius let vol = cylinderVolume 3.0 5.0 printfn " Volume: %g " vol
When you compile and execute the program, it yields the following output −
Volume: 141.372
Example 2
The following program returns the larger value of two given parameters −
// the function returns the larger value between two // arguments let max num1 num2 : int32 = // function body if(num1>num2)then num1 else num2 let res = max 39 52 printfn " Max Value: %d " res
When you compile and execute the program, it yields the following output −
Max Value: 52
Example 3
let doubleIt (x : int) = 2 * x printfn "Double 19: %d" ( doubleIt(19))
When you compile and execute the program, it yields the following output −
Double 19: 38
Recursive Functions
Recursive functions are functions that call themselves.
You define a recursive using the let rec keyword combination.
Syntax for defining a recursive function is −
//Recursive function definition let rec function-name parameter-list = recursive-function-body
For example −
let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)
Example 1
The following program returns Fibonacci 1 to 10 −
let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2) for i = 1 to 10 do printfn "Fibonacci %d: %d" i (fib i)
When you compile and execute the program, it yields the following output −
Fibonacci 1: 1 Fibonacci 2: 2 Fibonacci 3: 3 Fibonacci 4: 5 Fibonacci 5: 8 Fibonacci 6: 13 Fibonacci 7: 21 Fibonacci 8: 34 Fibonacci 9: 55 Fibonacci 10: 89
Example 2
The following program returns factorial 8 −
open System let rec fact x = if x < 1 then 1 else x * fact (x - 1) Console.WriteLine(fact 8)
When you compile and execute the program, it yields the following output −
40320
Arrow Notations in F#
F# reports about data type in functions and values, using a chained arrow notation. Let us take an example of a function that takes one int input, and returns a string. In arrow notation, it is written as −
int -> string
Data types are read from left to right.
Let us take another hypothetical function that takes two int data inputs and returns a string.
let mydivfunction x y = (x / y).ToString();;
F# reports the data type using chained arrow notation as −
val mydivfunction : x:int -> y:int -> string
The return type is represented by the rightmost data type in chained arrow notation.
Some more examples −
Notation | Meaning |
---|---|
float → float → float | The function takes two float inputs, returns another float. |
int → string → float | The function takes an int and a string input, returns a float. |
Lambda Expressions
A lambda expression is an unnamed function.
Let us take an example of two functions −
let applyFunction ( f: int -> int -> int) x y = f x y let mul x y = x * y let res = applyFunction mul 5 7 printfn "%d" res
When you compile and execute the program, it yields the following output −
35
Now in the above example, if instead of defining the function mul, we could have used lambda expressions as −
let applyFunction ( f: int -> int -> int) x y = f x y let res = applyFunction (fun x y -> x * y ) 5 7 printfn "%d" res
When you compile and execute the program, it yields the following output −
35
Function Composition and Pipelining
In F#, one function can be composed from other functions.
The following example shows the composition of a function named f, from two functions function1 and function2 −
let function1 x = x + 1 let function2 x = x * 5 let f = function1 >> function2 let res = f 10 printfn "%d" res
When you compile and execute the program, it yields the following output −
55
F# also provides a feature called pipelining of functions. Pipelining allows function calls to be chained together as successive operations.
The following example shows that −
let function1 x = x + 1 let function2 x = x * 5 let res = 10 |> function1 |> function2 printfn "%d" res
When you compile and execute the program, it yields the following output −
55
F# - Strings
In F#, the string type represents immutable text as a sequence of Unicode characters.
String Literals
String literals are delimited by the quotation mark (") character.
Some special characters are there for special uses like newline, tab, etc. They are encoded using backslash (\) character. The backslash character and the related character make the escape sequence. The following table shows the escape sequence supported by F#.
Character | Escape sequence |
---|---|
Backspace | \b |
Newline | \n |
Carriage return | \r |
Tab | \t |
Backslash | \\ |
Quotation mark | \" |
Apostrophe | \' |
Unicode character | \uXXXX or \UXXXXXXXX (where X indicates a hexadecimal digit) |
Ways of lgnoring the Escape Sequence
The following two ways makes the compiler ignore the escape sequence −
- Using the @ symbol.
- Enclosing the string in triple quotes.
When a string literal is preceded by the @ symbol, it is called a verbatim string. In that way, all escape sequences in the string are ignored, except that two quotation mark characters are interpreted as one quotation mark character.
When a string is enclosed by triple quotes, then also all escape sequences are ignored, including double quotation mark characters.
Example
The following example demonstrates this technique showing how to work with XML or other structures that include embedded quotation marks −
// Using a verbatim string let xmldata = @"<book author=""Lewis, C.S"" title=""Narnia"">" printfn "%s" xmldata
When you compile and execute the program, it yields the following output −
<book author="Lewis, C.S" title="Narnia">
Basic Operators on Strings
The following table shows the basic operations on strings −
Value | Description |
---|---|
collect : (char → string) → string → string | Creates a new string whose characters are the results of applying a specified function to each of the characters of the input string and concatenating the resulting strings. |
concat : string → seq<string> → string | Returns a new string made by concatenating the given strings with a separator. |
exists : (char → bool) → string → bool | Tests if any character of the string satisfies the given predicate. |
forall : (char → bool) → string → bool | Tests if all characters in the string satisfy the given predicate. |
init : int → (int → string) → string | Creates a new string whose characters are the results of applying a specified function to each index and concatenating the resulting strings. |
iter : (char → unit) → string → unit | Applies a specified function to each character in the string. |
iteri : (int → char → unit) → string → unit | Applies a specified function to the index of each character in the string and the character itself. |
length : string → int | Returns the length of the string. |
map : (char → char) → string → string | Creates a new string whose characters are the results of applying a specified function to each of the characters of the input string. |
mapi : (int → char → char) → string → string | Creates a new string whose characters are the results of applying a specified function to each character and index of the input string. |
replicate : int → string → string | Returns a string by concatenating a specified number of instances of a string. |
The following examples demonstrate the uses of some of the above functionalities −
Example 1
The String.collect function builds a new string whose characters are the results of applying a specified function to each of the characters of the input string and concatenating the resulting strings.
let collectTesting inputS = String.collect (fun c -> sprintf "%c " c) inputS printfn "%s" (collectTesting "Happy New Year!")
When you compile and execute the program, it yields the following output −
H a p p y N e w Y e a r !
Example 2
The String.concat function concatenates a given sequence of strings with a separator and returns a new string.
let strings = [ "Tutorials Point"; "Coding Ground"; "Absolute Classes" ] let ourProducts = String.concat "\n" strings printfn "%s" ourProducts
When you compile and execute the program, it yields the following output −
Tutorials Point Coding Ground Absolute Classes
Example 3
The String.replicate method returns a string by concatenating a specified number of instances of a string.
printfn "%s" <| String.replicate 10 "*! "
When you compile and execute the program, it yields the following output −
*! *! *! *! *! *! *! *! *! *!
F# - Options
The option type in F# is used in calculations when there may or may not exist a value for a variable or function. Option types are used for representing optional values in calculations. They can have two possible values − Some(x) or None.
For example, a function performing a division will return a value in normal situation, but will throw exceptions in case of a zero denominator. Using options here will help to indicate whether the function has succeeded or failed.
An option has an underlying type and can hold a value of that type, or it might not have a value.
Using Options
Let us take the example of division function. The following program explains this −
Let us write a function div, and send two arguments to it 20 and 5 −
let div x y = x / y let res = div 20 5 printfn "Result: %d" res
When you compile and execute the program, it yields the following output −
Result: 4
If the second argument is zero, then the program throws an exception −
let div x y = x / y let res = div 20 0 printfn "Result: %d" res
When you compile and execute the program, it yields the following output −
Unhandled Exception: System.DivideByZeroException: Division by zero
In such cases, we can use option types to return Some (value) when the operation is successful or None if the operation fails.
The following example demonstrates the use of options −
Example
let div x y = match y with | 0 -> None | _ -> Some(x/y) let res : int option = div 20 4 printfn "Result: %A " res
When you compile and execute the program, it yields the following output −
Result: Some 5
Option Properties and Methods
The option type supports the following properties and methods −
Property or method | Type | Description |
---|---|---|
None | 'T option | A static property that enables you to create an option value that has the None value. |
IsNone | bool | Returns true if the option has the None value. |
IsSome | bool | Returns true if the option has a value that is not None. |
Some | 'T option | A static member that creates an option that has a value that is not None. |
Value | 'T | Returns the underlying value, or throws a NullReferenceException if the value is None. |
Example 1
let checkPositive (a : int) = if a > 0 then Some(a) else None let res : int option = checkPositive(-31) printfn "Result: %A " res
When you compile and execute the program, it yields the following output −
Result: <null>
Example 2
let div x y = match y with | 0 -> None | _ -> Some(x/y) let res : int option = div 20 4 printfn "Result: %A " res printfn "Result: %A " res.Value
When you compile and execute the program, it yields the following output −
Result: Some 5 Result: 5
Example 3
let isHundred = function | Some(100) -> true | Some(_) | None -> false printfn "%A" (isHundred (Some(45))) printfn "%A" (isHundred (Some(100))) printfn "%A" (isHundred None)
When you compile and execute the program, it yields the following output −
false true false
F# - Tuples
A tuple is a comma-separated collection of values. These are used for creating ad hoc data structures, which group together related values.
For example, (“Zara Ali”, “Hyderabad”, 10) is a 3-tuple with two string values and an int value, it has the type (string * string * int).
Tuples could be pairs, triples, and so on, of the same or different types.
Some examples are provided here −
// Tuple of two integers. ( 4, 5 ) // Triple of strings. ( "one", "two", "three" ) // Tuple of unknown types. ( a, b ) // Tuple that has mixed types. ( "Absolute Classes", 1, 2.0 ) // Tuple of integer expressions. ( a * 4, b + 7)
Example
This program has a function that takes a tuple of four float values and returns the average −
let averageFour (a, b, c, d) = let sum = a + b + c + d sum / 4.0 let avg:float = averageFour (4.0, 5.1, 8.0, 12.0) printfn "Avg of four numbers: %f" avg
When you compile and execute the program, it yields the following output −
Avg of four numbers: 7.275000
Accessing Individual Tuple Members
The individual members of a tuple could be assessed and printed using pattern matching.
The following example illustrates the concept −
Example
let display tuple1 = match tuple1 with | (a, b, c) -> printfn "Detail Info: %A %A %A" a b c display ("Zara Ali", "Hyderabad", 10 )
When you compile and execute the program, it yields the following output −
Detail Info: "Zara Ali" "Hyderabad" 10
F# has two built-in functions, fst and snd, which return the first and second items in a 2-tuple.
The following example illustrates the concept −
Example
printfn "First member: %A" (fst(23, 30)) printfn "Second member: %A" (snd(23, 30)) printfn "First member: %A" (fst("Hello", "World!")) printfn "Second member: %A" (snd("Hello", "World!")) let nameTuple = ("Zara", "Ali") printfn "First Name: %A" (fst nameTuple) printfn "Second Name: %A" (snd nameTuple)
When you compile and execute the program, it yields the following output −
First member: 23 Second member: 30 First member: "Hello" Second member: "World!" First Name: "Zara" Second Name: "Ali"
F# - Records
A record is similar to a tuple, however it contains named fields. For example,
type website = { title : string; url : string }
Defining Record
A record is defined as a type using the type keyword, and the fields of the record are defined as a semicolon-separated list.
Syntax for defining a record is −
type recordName = { [ fieldName : dataType ] + }
Creating a Record
You can create a record by specifying the record's fields. For example, let us create a website record named homepage −
let homepage = { Title = "TutorialsPoint"; Url = "www.tutorialspoint.com" }
The following examples will explain the concepts −
Example 1
This program defines a record type named website. Then it creates some records of type website and prints the records.