F # - szybki przewodnik

F # to funkcjonalny język programowania. Aby zrozumieć konstrukcje języka F #, należy przeczytać kilka wierszy dotyczących nazwanego paradygmatu programowaniaFunctional Programming.

Programowanie funkcjonalne traktuje programy komputerowe jako funkcje matematyczne. W programowaniu funkcjonalnym skupiono by się na stałych i funkcjach zamiast na zmiennych i stanach. Ponieważ funkcje i stałe to rzeczy, które się nie zmieniają.

W programowaniu funkcjonalnym będziesz pisać programy modułowe, tj. Programy będą składać się z funkcji, które przyjmą inne funkcje jako dane wejściowe.

Programy napisane w funkcjonalnym języku programowania są zwykle zwięzłe.

O F #

Poniżej znajdują się podstawowe informacje o języku F # -

  • Został opracowany w 2005 roku w Microsoft Research.
  • Jest częścią rodziny języka .Net firmy Microsoft.
  • Jest to funkcjonalny język programowania.
  • Opiera się na funkcjonalnym języku programowania OCaml.

Funkcje języka F #

  • Jest to implementacja OCaml .Net.

  • Kompiluje .Net CLI (Common Language Interface) kod bajtowy lub MSIL (Microsoft Intermediate Language), który działa w środowisku CLR (Common Language Runtime).

  • Zapewnia wnioskowanie o typie.

  • Zapewnia bogate konstrukcje dopasowywania wzorców.

  • Posiada interaktywne możliwości tworzenia skryptów i debugowania.

  • Pozwala na pisanie funkcji wyższego rzędu.

  • Zapewnia dobrze rozwinięty model obiektowy.

Korzystanie z F #

F # jest zwykle używany w następujących obszarach -

  • Tworzenie modelu naukowego
  • Rozwiązywanie problemów matematycznych
  • Praca badawcza w zakresie sztucznej inteligencji
  • Modelowanie finansowe
  • Projekt graficzny
  • Konstrukcja procesora
  • Programowanie kompilatora
  • Telecommunications

Jest również używany w aplikacjach CRUD, stronach internetowych, grach GUI i innych programach ogólnego przeznaczenia.

W tym rozdziale omówiono narzędzia wymagane do programowania w języku F #.

Zintegrowane środowisko programistyczne (IDE) dla języka F #

Firma Microsoft udostępnia program Visual Studio 2013 do programowania w języku F #.

Bezpłatny program Visual Studio 2013 Community Edition jest dostępny w oficjalnej witrynie firmy Microsoft. Visual Studio 2013 Community i nowsze wersje są dostarczane z narzędziami Visual F #. Szczegóły instalacji dostępne w samouczku Asp.net . Narzędzia Visual F # obejmują kompilator wiersza polecenia (fsc.exe) i F # Interactive (fsi.exe).

Korzystając z tych narzędzi, można pisać wszelkiego rodzaju programy w języku F #, od prostych aplikacji wiersza polecenia po bardziej złożone aplikacje. Możesz również pisać pliki kodu źródłowego F # za pomocą podstawowego edytora tekstu, takiego jak Notatnik, i kompilować kod do zestawów za pomocą kompilatora wiersza polecenia.

Możesz go pobrać z Microsoft Visual Studio. Jest automatycznie instalowany na twoim komputerze.

Pisanie programów F # na łączach

Odwiedź oficjalną stronę F #, aby uzyskać najnowsze instrukcje dotyczące pobierania narzędzi jako pakietu Debiana lub kompilowania ich bezpośrednio ze źródła - https://fsharp.org/use/linux/.

F # to język programowania funkcjonalnego.

W języku F # funkcje działają jak typy danych. Możesz deklarować i używać funkcji w taki sam sposób, jak każdej innej zmiennej.

Ogólnie rzecz biorąc, aplikacja F # nie ma określonego punktu wejścia. Kompilator wykonuje wszystkie instrukcje najwyższego poziomu w pliku od góry do dołu.

Jednak zgodnie z proceduralnym stylem programowania wiele aplikacji zachowuje jedną instrukcję najwyższego poziomu, która wywołuje pętlę główną.

Poniższy kod przedstawia prosty program w języku F # -

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()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

sign 5: positive

Należy pamiętać, że -

  • Plik kodu F # może zaczynać się od liczby open instrukcje używane do importowania przestrzeni nazw.

  • Treść plików zawiera inne funkcje, które implementują logikę biznesową aplikacji.

  • Główna pętla zawiera najważniejsze instrukcje wykonywalne.

Widziałeś już podstawową strukturę programu F #, więc łatwo będzie zrozumieć inne podstawowe elementy składowe języka programowania F #.

Tokeny w F #

Program F # składa się z różnych tokenów. Token może być słowem kluczowym, identyfikatorem, stałą, literałem ciągu lub symbolem. Możemy podzielić tokeny F # na dwa typy -

  • Keywords
  • Symbol i operatory

Słowa kluczowe F #

W poniższej tabeli przedstawiono słowa kluczowe i krótkie opisy słów kluczowych. Użycie tych słów kluczowych omówimy w kolejnych rozdziałach.

Słowo kluczowe Opis
abstract Wskazuje metodę, która nie ma implementacji w typie, w którym jest zadeklarowana, lub jest wirtualna i ma domyślną implementację.
and Używany we wzajemnie cyklicznych powiązaniach, w deklaracjach właściwości i z wieloma ograniczeniami dotyczącymi parametrów ogólnych.
as Służy do nadania bieżącemu obiektowi klasy nazwy obiektu. Używane także do nadawania nazwy całemu wzorowi w dopasowaniu do wzorca.
assert Służy do weryfikacji kodu podczas debugowania.
base Używany jako nazwa obiektu klasy bazowej.
begin W pełnej składni wskazuje początek bloku kodu.
class W pełnej składni wskazuje początek definicji klasy.
default Wskazuje implementację metody abstrakcyjnej; używane razem z deklaracją metody abstrakcyjnej do tworzenia metody wirtualnej.
delegate Służy do deklarowania delegata.
do Używany w konstrukcjach zapętlonych lub do wykonywania kodu imperatywnego.
done W pełnej składni wskazuje koniec bloku kodu w wyrażeniu zapętlonym.
downcast Służy do konwersji na typ, który znajduje się niżej w łańcuchu dziedziczenia.
downto W for wyrażenie używane podczas liczenia w odwrotnej kolejności.
elif Używany w rozgałęzieniach warunkowych. Krótka forma else if.
else Używany w rozgałęzieniach warunkowych.
end

W definicjach typów i rozszerzeniach typów wskazuje koniec sekcji definicji elementów członkowskich.

W pełnej składni służy do określenia końca bloku kodu, który zaczyna się od słowa kluczowego begin.

exception Służy do deklarowania typu wyjątku.
extern Wskazuje, że zadeklarowany element programu jest zdefiniowany w innym pliku binarnym lub zestawie.
false Używany jako literał boolowski.
finally Używane razem z próbą wprowadzenia bloku kodu, który jest wykonywany niezależnie od tego, czy wystąpi wyjątek.
for Używany w konstrukcjach zapętlonych.
fun Używane w wyrażeniach lambda, zwanych również funkcjami anonimowymi.
function Używany jako krótsza alternatywa dla słowa kluczowego fun i wyrażenia dopasowującego w wyrażeniu lambda, które ma dopasowanie do wzorca dla pojedynczego argumentu.
global Służy do odwoływania się do obszaru nazw platformy .NET najwyższego poziomu.
if Używany w warunkowych konstrukcjach rozgałęziających.
in Używany do wyrażeń sekwencji i, w pełnej składni, do oddzielania wyrażeń od powiązań.
inherit Służy do określania klasy bazowej lub interfejsu podstawowego.
inline Służy do wskazania funkcji, która powinna zostać zintegrowana bezpośrednio z kodem dzwoniącego.
interface Służy do deklarowania i implementowania interfejsów.
internal Służy do określenia, że ​​element jest widoczny wewnątrz zespołu, ale nie poza nim.
lazy Służy do określenia obliczenia, które ma być wykonane tylko wtedy, gdy wymagany jest wynik.
let Służy do kojarzenia lub wiązania nazwy z wartością lub funkcją.
let! Używany w asynchronicznych przepływach pracy do wiązania nazwy z wynikiem obliczenia asynchronicznego lub, w innych wyrażeniach obliczeniowych, do wiązania nazwy z wynikiem, który jest typu obliczeniowego.
match Służy do rozgałęziania przez porównanie wartości ze wzorcem.
member Służy do deklarowania właściwości lub metody w typie obiektu.
module Służy do kojarzenia nazwy z grupą powiązanych typów, wartości i funkcji, aby logicznie oddzielić ją od innego kodu.
mutable Służy do deklarowania zmiennej, czyli wartości, którą można zmienić.
namespace Służy do kojarzenia nazwy z grupą powiązanych typów i modułów, aby logicznie oddzielić ją od innego kodu.
new

Służy do deklarowania, definiowania lub wywoływania konstruktora, który tworzy lub może utworzyć obiekt.

Używany również w ogólnych ograniczeniach parametrów, aby wskazać, że typ musi mieć określony konstruktor.

not Właściwie to nie słowo kluczowe. Jednak nie struct w połączeniu jest używany jako ogólne ograniczenie parametru.
null

Wskazuje na brak obiektu.

Używany również w ogólnych ograniczeniach parametrów.

of Używany w unii rozłącznych do wskazywania typu kategorii wartości oraz w deklaracjach delegatów i wyjątków.
open Służy do udostępniania zawartości przestrzeni nazw lub modułu bez kwalifikacji.
or

Używane z warunkami boolowskimi jako logiczne lub operator. Odpowiednik ||.

Używany również w ograniczeniach elementów.

override Służy do implementowania wersji metody abstrakcyjnej lub wirtualnej, która różni się od wersji podstawowej.
private Ogranicza dostęp do członka do kodu tego samego typu lub modułu.
public Umożliwia dostęp do członka spoza typu.
rec Służy do wskazania, że ​​funkcja jest rekurencyjna.
return Służy do wskazania wartości, która ma być podana jako wynik wyrażenia obliczenia.
return! Służy do wskazania wyrażenia obliczeniowego, które po oszacowaniu dostarcza wynik zawierającego wyrażenie obliczeniowe.
select Używane w wyrażeniach zapytań do określenia pól lub kolumn do wyodrębnienia. Zwróć uwagę, że jest to kontekstowe słowo kluczowe, co oznacza, że ​​w rzeczywistości nie jest to słowo zastrzeżone i działa jak słowo kluczowe tylko w odpowiednim kontekście.
static Służy do wskazania metody lub właściwości, które można wywołać bez wystąpienia typu lub elementu członkowskiego wartości, który jest współużytkowany przez wszystkie wystąpienia typu.
struct

Służy do deklarowania typu struktury.

Używany również w ogólnych ograniczeniach parametrów.

Używany w celu zapewnienia zgodności z OCaml w definicjach modułów.

then

Używane w wyrażeniach warunkowych.

Służy również do wywoływania efektów ubocznych po konstrukcji obiektu.

to Używane w pętlach for do wskazania zakresu.
true Używany jako literał boolowski.
try Służy do wprowadzania bloku kodu, który może generować wyjątek. Używany razem z lub ostatecznie.
type Służy do deklarowania klasy, rekordu, struktury, unii dyskryminowanej, typu wyliczenia, jednostki miary lub skrótu typu.
upcast Służy do konwersji na typ znajdujący się wyżej w łańcuchu dziedziczenia.
use Używane zamiast let dla wartości, które wymagają wywołania metody Dispose w celu zwolnienia zasobów.
use! Używane zamiast let! w asynchronicznych przepływach pracy i innych wyrażeniach obliczeniowych dla wartości, które wymagają wywołania metody Dispose w celu zwolnienia zasobów.
val Używany w podpisie, aby wskazać wartość lub w typie do zadeklarowania elementu członkowskiego, w ograniczonych sytuacjach.
void Wskazuje typ unieważnienia platformy .NET. Używany podczas współpracy z innymi językami .NET.
when Służy do warunków logicznych (gdy guards) na dopasowaniach wzorców i do wprowadzenia klauzuli ograniczenia dla parametru typu ogólnego.
while Wprowadza konstrukcję zapętloną.
with Używane razem ze słowem kluczowym match w wyrażeniach dopasowywania wzorców. Używany również w wyrażeniach obiektów, wyrażeniach kopiowania rekordów i rozszerzeniach typów w celu wprowadzenia definicji elementów członkowskich i wprowadzenia programów obsługi wyjątków.
yield Używany w wyrażeniu sekwencji do tworzenia wartości dla sekwencji.
yield! Używany w wyrażeniu obliczeniowym w celu dołączenia wyniku danego wyrażenia obliczeniowego do kolekcji wyników dla zawierającego wyrażenie obliczeniowe.

Niektóre zastrzeżone słowa kluczowe pochodzą z języka OCaml -

asr wylądować lor lsl lsr lxor mod sig

Niektóre inne zastrzeżone słowa kluczowe są zachowywane na potrzeby przyszłego rozszerzenia języka F #.

atomowy przerwa sprawdzone składnik konst przymus 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!

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

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

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 −

Show Example

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 −

Show Example

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 −

Show Example

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 −

Show Example

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.

Show Example

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

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).

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.

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

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 −

*! *! *! *! *! *! *! *! *! *!

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

ZA tupleto zbiór wartości oddzielonych przecinkami. Są one używane do tworzenia struktur danych ad hoc, które grupują powiązane wartości.

Na przykład („Zara Ali”, „Hyderabad”, 10) to trzyosobowa krotka z dwiema wartościami łańcuchowymi i wartością typu int, ma typ (string * string * int).

Krotki mogą być parami, trójkami i tak dalej tego samego lub różnych typów.

Oto kilka przykładów -

// 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)

Przykład

Ten program ma funkcję, która pobiera krotkę czterech wartości zmiennoprzecinkowych i zwraca średnią -

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

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Avg of four numbers: 7.275000

Dostęp do poszczególnych członków krotki

Poszczególnych członków krotki można było ocenić i wydrukować za pomocą dopasowania wzorców.

Poniższy przykład ilustruje koncepcję -

Przykład

let display tuple1 =
   match tuple1 with
   | (a, b, c) -> printfn "Detail Info: %A %A %A" a b c

display ("Zara Ali", "Hyderabad", 10 )

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Detail Info: "Zara Ali" "Hyderabad" 10

F # ma dwie wbudowane funkcje, fst i snd, które zwracają pierwszy i drugi element w podwójnej krotce.

Poniższy przykład ilustruje koncepcję -

Przykład

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)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

First member: 23
Second member: 30
First member: "Hello"
Second member: "World!"
First Name: "Zara"
Second Name: "Ali"

ZA recordjest podobny do krotki, ale zawiera nazwane pola. Na przykład,

type website =
   { title : string;
      url : string }

Definiowanie rekordu

Rekord jest definiowany jako typ przy użyciu rozszerzenia type słowo kluczowe, a pola rekordu są zdefiniowane jako lista oddzielona średnikami.

Składnia definiowania rekordu to -

type recordName =
   { [ fieldName : dataType ] + }

Tworzenie rekordu

Możesz utworzyć rekord, określając jego pola. Na przykład stwórzmy rekord witryny o nazwie strona główna -

let homepage = { Title = "TutorialsPoint"; Url = "www.tutorialspoint.com" }

Poniższe przykłady wyjaśnią pojęcia -

Przykład 1

Ten program definiuje typ rekordu o nazwie witryna internetowa. Następnie tworzy rekordy typu „witryna internetowa” i drukuje je.

(* defining a record type named website *)
type website =
   { Title : string;
      Url : string }

(* creating some records *)
let homepage = { Title = "TutorialsPoint"; Url = "www.tutorialspoint.com" }
let cpage = { Title = "Learn C"; Url = "www.tutorialspoint.com/cprogramming/index.htm" }
let fsharppage = { Title = "Learn F#"; Url = "www.tutorialspoint.com/fsharp/index.htm" }
let csharppage = { Title = "Learn C#"; Url = "www.tutorialspoint.com/csharp/index.htm" }

(*printing records *)
(printfn "Home Page: Title: %A \n \t URL: %A") homepage.Title homepage.Url
(printfn "C Page: Title: %A \n \t URL: %A") cpage.Title cpage.Url
(printfn "F# Page: Title: %A \n \t URL: %A") fsharppage.Title fsharppage.Url
(printfn "C# Page: Title: %A \n \t URL: %A") csharppage.Title csharppage.Url

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Home Page: Title: "TutorialsPoint"
       URL: "www.tutorialspoint.com"
C Page: Title: "Learn C"
      URL: "www.tutorialspoint.com/cprogramming/index.htm"
F# Page: Title: "Learn F#"
      URL: "www.tutorialspoint.com/fsharp/index.htm"
C# Page: Title: "Learn C#"
      URL: "www.tutorialspoint.com/csharp/index.htm"

Przykład 2

type student =
   { Name : string;
      ID : int;
      RegistrationText : string;
      IsRegistered : bool }

let getStudent name id =
   { Name = name; ID = id; RegistrationText = null; IsRegistered = false }

let registerStudent st =
   { st with
      RegistrationText = "Registered";
      IsRegistered = true }

let printStudent msg st =
   printfn "%s: %A" msg st

let main() =
   let preRegisteredStudent = getStudent "Zara" 10
   let postRegisteredStudent = registerStudent preRegisteredStudent

   printStudent "Before Registration: " preRegisteredStudent
   printStudent "After Registration: " postRegisteredStudent

main()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Before Registration: : {Name = "Zara";
   ID = 10;
   RegistrationText = null;
   IsRegistered = false;}
After Registration: : {Name = "Zara";
   ID = 10;
   RegistrationText = "Registered";
   IsRegistered = true;}

W języku F # lista jest uporządkowaną, niezmienną serią elementów tego samego typu. Jest to do pewnego stopnia równoważne strukturze danych z połączonej listy.

Moduł F #, Microsoft.FSharp.Collections.List,ma wspólne operacje na listach. Jednak F # importuje ten moduł automatycznie i udostępnia go każdej aplikacji F #.

Tworzenie i inicjowanie listy

Poniżej przedstawiono różne sposoby tworzenia list -

  • Korzystanie z listy literals.

  • Za pomocą cons (: :) operator.

  • Używając List.init metoda modułu List.

  • Korzystanie z niektórych syntactic constructs nazywa List Comprehensions.

Lista literałów

W tej metodzie wystarczy określić rozdzieloną średnikami sekwencję wartości w nawiasach kwadratowych. Na przykład -

let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

Wady (: :) Operator

Za pomocą tej metody można dodać niektóre wartości, poprzedzając lub cons-ingdo istniejącej listy przy użyciu operatora ::. Na przykład -

let list2 = 1::2::3::4::5::6::7::8::9::10::[];;

[] oznacza pustą listę.

List init Method

Metoda List.init modułu List jest często używana do tworzenia list. Ta metoda ma typ -

val init : int -> (int -> 'T) -> 'T list

Pierwszy argument to żądana długość nowej listy, a drugi argument to funkcja inicjująca, która generuje pozycje na liście.

Na przykład,

let list5 = List.init 5 (fun index -> (index, index * index, index * index * index))

Tutaj funkcja indeksu generuje listę.

Lista ujęć

Listy składane to specjalne konstrukcje składniowe używane do generowania list.

Składnia składni listy F # występuje w dwóch formach - zakresów i generatorów.

Zakresy mają konstrukcje - [start .. end] i [start .. step .. end]

Na przykład,

let list3 = [1 .. 10]

Generatory mają konstrukcję - [for x in collection do ... yield expr]

Na przykład,

let list6 = [ for a in 1 .. 10 do yield (a * a) ]

Ponieważ yield słowo kluczowe wypycha pojedynczą wartość na listę, słowo kluczowe, yield!, umieszcza zbiór wartości na liście.

Poniższa funkcja demonstruje powyższe metody -

Przykład

(* using list literals *)
let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1

(*using cons operator *)
let list2 = 1 :: 2 :: 3 :: []
printfn "The list: %A" list2

(* using range constructs*)
let list3 = [1 .. 10]
printfn "The list: %A" list3

(* using range constructs *)
let list4 = ['a' .. 'm']
printfn "The list: %A" list4

(* using init method *)
let list5 = List.init 5 (fun index -> (index, index * index, index * index * index))
printfn "The list: %A" list5

(* using yield operator *)
let list6 = [ for a in 1 .. 10 do yield (a * a) ]
printfn "The list: %A" list6

(* using yield operator *)
let list7 = [ for a in 1 .. 100 do if a % 3 = 0 && a % 5 = 0 then yield a]
printfn "The list: %A" list7

(* using yield! operator *)
let list8 = [for a in 1 .. 3 do yield! [ a .. a + 3 ] ]
printfn "The list: %A" list8

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The list: [1; 2; 3]
The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The list: ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm']
The list: [(0, 0, 0); (1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64)]
The list: [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
The list: [15; 30; 45; 60; 75; 90]
The list: [1; 2; 3; 4; 2; 3; 4; 5; 3; 4; 5; 6]

Właściwości typu danych listy

W poniższej tabeli przedstawiono różne właściwości typu danych listy -

własność Rodzaj Opis
Głowa „T Pierwszy element.
Pusty Lista 'T Właściwość statyczna, która zwraca pustą listę odpowiedniego typu.
Jest pusty bool true jeśli lista nie zawiera elementów.
Pozycja „T Element o określonym indeksie (liczony od zera).
Długość int Liczba elementów.
Ogon Lista 'T Lista bez pierwszego elementu.

Poniższy przykład pokazuje użycie tych właściwości -

Przykład

let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]

// Use of Properties
printfn "list1.IsEmpty is %b" (list1.IsEmpty)
printfn "list1.Length is %d" (list1.Length)
printfn "list1.Head is %d" (list1.Head)
printfn "list1.Tail.Head is %d" (list1.Tail.Head)
printfn "list1.Tail.Tail.Head is %d" (list1.Tail.Tail.Head)
printfn "list1.Item(1) is %d" (list1.Item(1))

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

list1.IsEmpty is false
list1.Length is 8
list1.Head is 2
list1.Tail.Head is 4
list1.Tail.Tail.Head is 6
list1.Item(1) is 4

Podstawowe operatory na liście

Poniższa tabela przedstawia podstawowe operacje na typie danych listy -

Wartość Opis
dołącz: 'Lista T →' Lista T → 'Lista T. Zwraca nową listę zawierającą elementy pierwszej listy, po których następują elementy drugiej.
średnia: 'T lista → ^ T Zwraca średnią elementów na liście.
średnia Przez: ('T → ^ U) →' T lista → ^ U Zwraca średnią z elementów wygenerowanych przez zastosowanie funkcji do każdego elementu listy.
wybierz: (opcja 'T →' U) → 'Lista T →' Lista U Stosuje daną funkcję do każdego elementu listy. Zwraca listę zawierającą wyniki dla każdego elementu, z którego funkcja zwracaSome.
zbierać: ('T →' Lista U) → 'Lista T →' Lista U Do każdego elementu listy stosuje daną funkcję. Łączy wszystkie wyniki i zwraca połączoną listę.
concat: seq <'T lista> →' T lista Zwraca nową listę, która zawiera elementy każdej z list w kolejności.
puste: 'T lista Zwraca pustą listę podanego typu.
istnieje: ('T → bool) →' T lista → bool Sprawdza, czy którykolwiek element listy spełnia podany predykat.
istnieje2: ('T1 →' T2 → bool) → 'Lista T1 →' Lista T2 → bool Sprawdza, czy jakakolwiek para odpowiednich elementów list spełnia podany predykat.
filtr: ('T → bool) →' T lista → 'T lista Zwraca nową kolekcję zawierającą tylko elementy kolekcji, dla których zwraca dany predykat true.
znajdź: ('T → bool) →' Lista T → 'T Zwraca pierwszy element, dla którego zwraca dana funkcja true.
findIndex: ('T → bool) →' T lista → int Zwraca indeks pierwszego elementu na liście, który spełnia podany predykat.
fold: ('Stan →' T → 'Stan) →' Stan → 'Lista T →' Stan Stosuje funkcję do każdego elementu kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Ta funkcja przyjmuje drugi argument i stosuje do niego funkcję oraz pierwszy element listy. Następnie przekazuje ten wynik do funkcji wraz z drugim elementem i tak dalej. Na koniec zwraca wynik końcowy. Jeśli funkcją wejściową jest f, a elementy są i0 ... iN, to ta funkcja oblicza f (... (fs i0) i1 ...) iN.
fold2: ('Stan →' T1 → 'T2 →' Stan) → 'Stan →' Lista T1 → 'Lista T2 →' Stan Stosuje funkcję do odpowiednich elementów dwóch kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Kolekcje muszą mieć identyczne rozmiary. Jeśli funkcją wejściową jest f, a elementy to i0 ... iN oraz j0 ... jN, to ta funkcja oblicza f (... (fs i0 j0) ...) iN jN.
foldBack: ('T →' Stan → 'Stan) →' Lista T → 'Stan →' Stan Stosuje funkcję do każdego elementu kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Jeśli funkcja wejściowa isf, a elementy to i0 ... iN, oblicza f i0 (... (f iN s)).
foldBack2: ('T1 →' T2 → 'Stan →' Stan) → 'Lista T1 →' Lista T2 → 'Stan →' Stan Stosuje funkcję do odpowiednich elementów dwóch kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Kolekcje muszą mieć identyczne rozmiary. Jeśli funkcją wejściową jest f, a elementy to i0 ... iN oraz j0 ... jN, to ta funkcja oblicza f i0 j0 (... (f iN jN s)).
forall: ('T → bool) →' T lista → bool Sprawdza, czy wszystkie elementy kolekcji spełniają podany predykat.
forall2: ('T1 →' T2 → bool) → 'Lista T1 →' Lista T2 → bool Sprawdza, czy wszystkie odpowiadające elementy kolekcji spełniają podany predykat parami.
head: 'T lista →' T Zwraca pierwszy element listy.
init: int → (int → 'T) →' T list Tworzy listę poprzez wywołanie danego generatora w każdym indeksie.
isEmpty: 'T lista → bool Zwroty true jeśli lista nie zawiera elementów, false Inaczej.
iter: ('T → jednostka) →' Lista T → jednostka Stosuje daną funkcję do każdego elementu kolekcji.
iter2: ('T1 →' T2 → jednostka) → 'Lista T1 →' Lista T2 → jednostka Stosuje daną funkcję do dwóch kolekcji jednocześnie. Kolekcje muszą mieć identyczny rozmiar.
iteri: (int → 'T → jednostka) →' T lista → jednostka Stosuje daną funkcję do każdego elementu kolekcji. Liczba całkowita przekazana do funkcji wskazuje indeks elementu.
iteri2: (int → 'T1 →' T2 → jednostka) → 'Lista T1 →' Lista T2 → jednostka Stosuje daną funkcję do dwóch kolekcji jednocześnie. Kolekcje muszą mieć identyczny rozmiar. Liczba całkowita przekazana do funkcji wskazuje indeks elementu.
długość: 'T lista → wew Zwraca długość listy.
mapa: ('T →' U) → 'Lista T →' Lista U Tworzy nową kolekcję, której elementy są wynikiem zastosowania danej funkcji do każdego z elementów kolekcji.
mapa2: ('T1 →' T2 → 'U) →' Lista T1 → 'Lista T2 →' Lista U Tworzy nową kolekcję, której elementy są wynikiem zastosowania danej funkcji do odpowiednich elementów dwóch kolekcji parami.
map3: ('T1 →' T2 → 'T3 →' U) → 'Lista T1 →' Lista T2 → 'Lista T3 →' Lista U Tworzy nową kolekcję, której elementy są wynikiem zastosowania danej funkcji do odpowiednich elementów trzech kolekcji jednocześnie.
mapi: (int → 'T →' U) → 'Lista T →' Lista U Tworzy nową kolekcję, której elementy są wynikiem zastosowania danej funkcji do każdego z elementów kolekcji. Indeks będący liczbą całkowitą przekazaną do funkcji wskazuje indeks (od 0) transformowanego elementu.
mapi2: (int → 'T1 →' T2 → 'U) →' Lista T1 → 'Lista T2 →' Lista U Podobnie jak List.mapi, ale mapuje odpowiednie elementy z dwóch list o równej długości.
max: 'T lista →' T Zwraca największy ze wszystkich elementów listy w porównaniu przy użyciu Operators.max.
maxBy: ('T →' U) → 'Lista T →' T Zwraca największy ze wszystkich elementów listy w porównaniu przy użyciu metody Operators.max w wyniku funkcji.
min: 'T lista →' T Zwraca najniższy ze wszystkich elementów listy w porównaniu przy użyciu Operators.min.
min Przez: ('T →' U) → 'Lista T →' T Zwraca najniższy ze wszystkich elementów listy w porównaniu przy użyciu Operators.min w wyniku funkcji
nth: 'T lista → int →' T Indeksy do listy. Pierwszy element ma indeks 0.
ofArray: 'T [] →' T lista Tworzy listę z podanej tablicy.
ofSeq: seq <'T> →' T list Tworzy nową listę z podanego wyliczalnego obiektu.
partycja: ('T → bool) →' T lista * 'T lista Dzieli kolekcję na dwie kolekcje zawierające elementy, dla których zwraca dany predykat true i false odpowiednio.
permute: (int → int) → 'T lista →' T lista Zwraca listę ze wszystkimi elementami permutowanymi zgodnie z określoną permutacją.
wybierz: (opcja 'T →' U) → 'Lista T →' U Stosuje daną funkcję do kolejnych elementów, zwracając pierwszy wynik, gdy funkcja zwraca Some za jakąś wartość.
zmniejsz: ('T →' T → 'T) →' Lista T → 'T Stosuje funkcję do każdego elementu kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Ta funkcja stosuje określoną funkcję do pierwszych dwóch elementów listy. Następnie przekazuje ten wynik do funkcji wraz z trzecim elementem i tak dalej. Na koniec zwraca wynik końcowy. Jeśli funkcją wejściową jest f, a elementy są i0 ... iN, to funkcja ta oblicza f (... (f i0 i1) i2 ...) iN.
redukcjaBack: ('T →' T → 'T) →' Lista T → 'T Stosuje funkcję do każdego elementu kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Jeśli funkcja wejściowa isf, a elementy to i0 ... iN, to ta funkcja oblicza f i0 (... (f iN-1 iN)).
replicate: (int → 'T →' T list) Tworzy listę poprzez wywołanie danego generatora w każdym indeksie.
rev: 'Lista T →' Lista T. Zwraca nową listę z elementami w odwrotnej kolejności.
skanowanie: ('Stan →' T → 'Stan) →' Stan → 'Lista T →' Lista stanów Stosuje funkcję do każdego elementu kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Ta funkcja przyjmuje drugi argument i stosuje do niego określoną funkcję oraz pierwszy element listy. Następnie przekazuje ten wynik do funkcji wraz z drugim elementem i tak dalej. Na koniec zwraca listę wyników pośrednich i wyniku końcowego.
scanBack: ('T →' Stan → 'Stan) →' Lista T → 'Stan →' Lista stanów Podobnie jak foldBack, ale zwraca wyniki pośrednie i końcowe
sortuj: 'T lista →' T lista Sortuje podaną listę za pomocą Operators.compare.
sortBy: ('T →' Key) → 'T list →' T list Sortuje podaną listę za pomocą kluczy podanych przez daną projekcję. Klucze są porównywane za pomocą Operators.compare.
sortWith: ('T →' T → int) → 'T lista →' T lista Sortuje podaną listę przy użyciu podanej funkcji porównania.
suma: ^ T lista → ^ T Zwraca sumę elementów na liście.
sumBy: ('T → ^ U) →' T lista → ^ U Zwraca sumę wyników wygenerowanych przez zastosowanie funkcji do każdego elementu listy.
ogon: 'Lista T →' Lista T. Zwraca listę wejściową bez pierwszego elementu.
toArray: 'T lista →' T [] Tworzy tablicę z podanej listy.
toSeq: 'T list → seq <' T> Wyświetla podaną listę jako sekwencję.
tryFind: ('T → bool) →' Lista T → opcja 'T Zwraca pierwszy element, dla którego zwraca dana funkcja true. PowrótNone jeśli taki element nie istnieje.
tryFindIndex: ('T → bool) →' T lista → opcja int Zwraca indeks pierwszego elementu na liście, który spełnia podany predykat. PowrótNone jeśli taki element nie istnieje.
tryPick: (opcja 'T →' U) → 'Lista T → opcja' U Stosuje daną funkcję do kolejnych elementów, zwracając pierwszy wynik, gdy funkcja zwraca Someza jakąś wartość. Jeśli taki element nie istnieje, powróćNone.
rozpakuj: ('T1 *' T2) lista → 'T1 lista *' Lista T2 Dzieli listę par na dwie listy.
unzip3: ('T1 *' T2 * 'T3) lista →' Lista T1 * 'Lista T2 *' Lista T3 Dzieli listę trójek na trzy listy.
zip: 'Lista T1 →' Lista T2 → ('T1 *' T2) lista Łączy dwie listy w listę par. Obie listy muszą mieć równe długości.
zip3: 'Lista T1 →' Lista T2 → 'Lista T3 → (' T1 * 'T2 *' T3) lista Łączy trzy listy w listę trójek. Listy muszą mieć równe długości.

Poniższe przykłady pokazują zastosowania powyższych funkcji -

Przykład 1

Ten program pokazuje rekurencyjne odwracanie listy -

let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]
printfn "The original list: %A" list1

let reverse lt =
   let rec loop acc = function
      | [] -> acc
      | hd :: tl -> loop (hd :: acc) tl
   loop [] lt

printfn "The reversed list: %A" (reverse list1)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The original list: [2; 4; 6; 8; 10; 12; 14; 16]
The reversed list: [16; 14; 12; 10; 8; 6; 4; 2]

Możesz jednak użyć rev funkcja modułu w tym samym celu -

let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]
printfn "The original list: %A" list1
printfn "The reversed list: %A" (List.rev list1)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The original list: [2; 4; 6; 8; 10; 12; 14; 16]
The reversed list: [16; 14; 12; 10; 8; 6; 4; 2]

Przykład 2

Ten program pokazuje filtrowanie listy przy użyciu rozszerzenia List.filter metoda -

let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1
let list2 = list1 |> List.filter (fun x -> x % 2 = 0);;
printfn "The Filtered list: %A" list2

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The Filtered list: [2; 4; 6; 8; 10]

Przykład 3

Plik List.map metoda odwzorowuje listę z jednego typu na inny -

let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1
let list2 = list1 |> List.map (fun x -> (x * x).ToString());;
printfn "The Mapped list: %A" list2

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The Mapped list: ["1"; "4"; "9"; "16"; "25"; "36"; "49"; "64"; "81"; "100"]

Przykład 4

Plik List.append metoda, a operator @ dołącza jedną listę do drugiej -

let list1 = [1; 2; 3; 4; 5 ]
let list2 = [6; 7; 8; 9; 10]
let list3 = List.append list1 list2

printfn "The first list: %A" list1
printfn "The second list: %A" list2
printfn "The appened list: %A" list3

let lt1 = ['a'; 'b';'c' ]
let lt2 = ['e'; 'f';'g' ]
let lt3 = lt1 @ lt2

printfn "The first list: %A" lt1
printfn "The second list: %A" lt2
printfn "The appened list: %A" lt3

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The first list: [1; 2; 3; 4; 5]
The second list: [6; 7; 8; 9; 10]
The appened list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The first list: ['a'; 'b'; 'c']
The second list: ['e'; 'f'; 'g']
The appened list: ['a'; 'b'; 'c'; 'e'; 'f'; 'g']

Przykład 5

Plik List.sortmetoda sortuje listę. PlikList.sum zwraca sumę elementów na liście i List.average metoda podaje średnią elementów na liście -

let list1 = [9.0; 0.0; 2.0; -4.5; 11.2; 8.0; -10.0]
printfn "The list: %A" list1

let list2 = List.sort list1
printfn "The sorted list: %A" list2

let s = List.sum list1
let avg = List.average list1
printfn "The sum: %f" s
printfn "The average: %f" avg

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The list: [9.0; 0.0; 2.0; -4.5; 11.2; 8.0; -10.0]
The sorted list: [-10.0; -4.5; 0.0; 2.0; 8.0; 9.0; 11.2]
The sum: 15.700000
The average: 2.242857

Operacja „zwijania” stosuje funkcję do każdego elementu na liście, agreguje wynik funkcji w zmiennej akumulatorowej i zwraca akumulator jako wynik operacji zwinięcia.

Przykład 6

Plik List.fold metoda stosuje funkcję do każdego elementu od lewej do prawej, podczas gdy List.foldBack stosuje funkcję do każdego elementu od prawej do lewej.

let sumList list = List.fold (fun acc elem -> acc + elem) 0 list
printfn "Sum of the elements of list %A is %d." [ 1 .. 10 ] (sumList [ 1 .. 10 ])

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Sum of the elements of list [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] is 55.

Sekwencje, podobnie jak listy, również reprezentują uporządkowany zbiór wartości. Jednak elementy w sekwencji lub wyrażeniu sekwencji są obliczane, gdy jest to wymagane. Nie są one obliczane od razu iz tego powodu są używane do reprezentowania nieskończonych struktur danych.

Definiowanie sekwencji

Sekwencje są definiowane przy użyciu następującej składni -

seq { expr }

Na przykład,

let seq1 = seq { 1 .. 10 }

Tworzenie sekwencji i wyrażeń sekwencji

Podobnie jak w przypadku list, możesz tworzyć sekwencje przy użyciu zakresów i wyrażeń.

Wyrażenia sekwencji to wyrażenia, które można napisać w celu tworzenia sekwencji. Można to zrobić -

  • Określając zakres.
  • Określając zakres ze zwiększaniem lub zmniejszaniem.
  • Korzystając z yield słowo kluczowe, aby wygenerować wartości, które staną się częścią sekwencji.
  • Używając operatora →.

Poniższe przykłady ilustrują koncepcję -

Przykład 1

(* Sequences *)
let seq1 = seq { 1 .. 10 }

(* ascending order and increment*)
printfn "The Sequence: %A" seq1
let seq2 = seq { 1 .. 5 .. 50 }

(* descending order and decrement*)
printfn "The Sequence: %A" seq2
let seq3 = seq {50 .. -5 .. 0}
printfn "The Sequence: %A" seq3

(* using yield *)
let seq4 = seq { for a in 1 .. 10 do yield a, a*a, a*a*a }
printfn "The Sequence: %A" seq4

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The Sequence: seq [1; 2; 3; 4; ...]
The Sequence: seq [1; 6; 11; 16; ...]
The Sequence: seq [50; 45; 40; 35; ...]
The Sequence: seq [(1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64); ...]

Przykład 2

Poniższy program wypisuje liczby pierwsze od 1 do 50 -

(* Recursive isprime function. *)
let isprime n =
   let rec check i =
      i > n/2 || (n % i <> 0 && check (i + 1))
   check 2

let primeIn50 = seq { for n in 1..50 do if isprime n then yield n }
for x in primeIn50 do
   printfn "%d" x

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

1
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47

Podstawowe operacje na sekwencji

W poniższej tabeli przedstawiono podstawowe operacje na sekwencjach typu danych -

Wartość Opis
append: seq <'T> → seq <' T> → seq <'T> Zawija dwa podane wyliczenia jako pojedyncze połączone wyliczenie.
średnia: seq <^ T> → ^ T Zwraca średnią elementów w sekwencji.
średnia Przez: ('T → ^ U) → seq <' T> → ^ U Zwraca średnią wyników wygenerowanych przez zastosowanie funkcji do każdego elementu sekwencji.
cache: seq <'T> → seq <' T> Zwraca sekwencję, która odpowiada buforowanej wersji sekwencji wejściowej.
cast: IEnumerable → seq <'T> Zawija luźno wpisany System. Sekwencja kolekcji jako sekwencja wpisana.
wybierz: (opcja 'T →' U) → seq <'T> → seq <' U> Stosuje daną funkcję do każdego elementu listy. Zwróć listę zawierającą wyniki dla każdego elementu, z którego funkcja zwracaSome.
collect: ('T →' Collection) → seq <'T> → seq <' U> Stosuje daną funkcję do każdego elementu sekwencji i łączy wszystkie wyniki.
porównaj Z: ('T →' T → int) → seq <'T> → seq <' T> → int Porównuje dwie sekwencje przy użyciu danej funkcji porównania, element po elemencie.
concat: seq <'Collection> → seq <' T> Łączy podane wyliczenia jako pojedyncze połączone wyliczenie.
countBy: ('T →' Key) → seq <'T> → seq <' Key * int> Stosuje funkcję generującą klucz do każdego elementu sekwencji i zwraca sekwencję dającą unikatowe klucze i ich liczbę wystąpień w oryginalnej sekwencji.
opóźnienie: (unit → seq <'T>) → seq <' T> Zwraca sekwencję utworzoną na podstawie podanej opóźnionej specyfikacji sekwencji.
odrębny: seq <'T> → seq <' T> Zwraca sekwencję, która nie zawiera zduplikowanych wpisów zgodnie z ogólnymi porównaniami skrótów i równości wpisów. Jeśli element występuje wiele razy w sekwencji, późniejsze wystąpienia są odrzucane.
odrębnyBy: (klawisz 'T →') → seq <'T> → seq <' T> Zwraca sekwencję, która nie zawiera zduplikowanych wpisów zgodnie z ogólnym skrótem i porównaniami równości kluczy zwróconych przez daną funkcję generującą klucz. Jeśli element występuje wiele razy w sekwencji, późniejsze wystąpienia są odrzucane.
pusty: seq <'T> Tworzy pustą sekwencję.
dokładnie jeden: seq <'T> →' T Zwraca jedyny element sekwencji.
istnieje: ('T → bool) → seq <' T> → bool Sprawdza, czy jakikolwiek element ciągu spełnia podany predykat.
istnieje2: ('T1 →' T2 → bool) → seq <'T1> → seq <' T2> → bool Sprawdza, czy jakakolwiek para odpowiednich elementów sekwencji wejściowych spełnia podany predykat.
filter: ('T → bool) → seq <' T> → seq <'T> Zwraca nową kolekcję zawierającą tylko elementy kolekcji, dla których zwraca dany predykat true.
znajdź: ('T → bool) → seq <' T> → 'T Zwraca pierwszy element, dla którego zwraca dana funkcja true.
findIndex: ('T → bool) → seq <' T> → int Zwraca indeks pierwszego elementu, dla którego zwraca dana funkcja true.
fold: ('Stan →' T → 'Stan) →' Stan → sekwencja <'T> →' Stan Stosuje funkcję do każdego elementu kolekcji, tworząc wątek argumentu akumulatora przez obliczenia. Jeśli funkcją wejściową jest f, a elementy to i0 ... iN, to ta funkcja oblicza f (... (fs i0) ...) iN.
forall: ('T → bool) → seq <' T> → bool Sprawdza, czy wszystkie elementy ciągu spełniają podany predykat.
forall2: ('T1 →' T2 → bool) → seq <'T1> → seq <' T2> → bool Testuje wszystkie pary elementów narysowanych z dwóch sekwencji, które spełniają zadany predykat. Jeśli jedna sekwencja jest krótsza od drugiej, pozostałe elementy dłuższej sekwencji są ignorowane.
groupBy: ('T →' Key) → seq <'T> → seq <' Key * seq <'T >> Stosuje funkcję generującą klucz do każdego elementu sekwencji i zwraca sekwencję unikatowych kluczy. Każdy unikalny klucz zawiera również sekwencję wszystkich elementów, które pasują do tego klucza.
head: seq <'T> →' T Zwraca pierwszy element sekwencji.
init: int → (int → 'T) → seq <' T> Generuje nową sekwencję, która po wykonaniu iteracji zwraca kolejne elementy, wywołując daną funkcję, aż do podanej liczby. Wyniki wywołania funkcji nie są zapisywane, to znaczy funkcja jest ponownie stosowana w razie potrzeby do ponownego wygenerowania elementów. Funkcja otrzymuje indeks generowanego elementu.
initInfinite: (int → 'T) → seq <' T> Generuje nową sekwencję, która po wykonaniu iteracji zwróci kolejne elementy, wywołując daną funkcję. Wyniki wywołania funkcji nie są zapisywane, to znaczy funkcja zostanie ponownie zastosowana w razie potrzeby do ponownego wygenerowania elementów. Funkcja otrzymuje indeks generowanego elementu.
isEmpty: seq <'T> → bool Sprawdza, czy sekwencja ma jakieś elementy.
iter: ('T → unit) → seq <' T> → unit Stosuje daną funkcję do każdego elementu kolekcji.
iter2: ('T1 →' T2 → unit) → seq <'T1> → seq <' T2> → unit Stosuje daną funkcję do dwóch kolekcji jednocześnie. Jeśli jedna sekwencja jest krótsza od drugiej, pozostałe elementy dłuższej sekwencji są ignorowane.
iteri: (int → 'T → unit) → seq <' T> → unit Stosuje daną funkcję do każdego elementu kolekcji. Liczba całkowita przekazana do funkcji wskazuje indeks elementu.
ostatnia: seq <'T> →' T Zwraca ostatni element sekwencji.
length: seq <'T> → int Zwraca długość sekwencji.
map: ('T →' U) → seq <'T> → seq <' U> Tworzy nową kolekcję, której elementy są wynikiem zastosowania danej funkcji do każdego z elementów kolekcji. Podana funkcja zostanie zastosowana, gdy elementy są żądane przy użyciu metody MoveNext na modułach wyliczających pobranych z obiektu.
map2: ('T1 →' T2 → 'U) → seq <' T1> → seq <'T2> → seq <' U> Tworzy nową kolekcję, której elementy są wynikiem zastosowania danej funkcji do odpowiednich par elementów z dwóch sekwencji. Jeśli jedna sekwencja wejściowa jest krótsza od drugiej, pozostałe elementy dłuższej sekwencji są ignorowane.
mapi: (int → 'T →' U) → seq <'T> → seq <' U> Tworzy nową kolekcję, której elementy są wynikiem zastosowania danej funkcji do każdego z elementów kolekcji. Indeks będący liczbą całkowitą przekazaną do funkcji wskazuje indeks (od 0) transformowanego elementu.
max: seq <'T> →' T Zwraca największy ze wszystkich elementów sekwencji w porównaniu przy użyciu Operators.max.
maxBy: ('T →' U) → seq <'T> →' T Zwraca największy ze wszystkich elementów sekwencji w porównaniu przy użyciu Operators.max w wyniku funkcji.
min: seq <'T> →' T Zwraca najniższy ze wszystkich elementów sekwencji w porównaniu przy użyciu Operators.min.
min By: ('T →' U) → seq <'T> →' T Zwraca najniższy ze wszystkich elementów sekwencji w porównaniu przy użyciu Operators.min w wyniku funkcji.
nth: int → seq <'T> →' T Oblicza n-ty element w kolekcji.
ofArray: 'T array → seq <' T> Wyświetla daną tablicę jako sekwencję.
ofList: 'T lista → seq <' T> Wyświetla podaną listę jako sekwencję.
parami: seq <'T> → seq <' T * 'T> Zwraca sekwencję każdego elementu w sekwencji wejściowej i jego poprzednika, z wyjątkiem pierwszego elementu, który jest zwracany tylko jako poprzednik drugiego elementu.
wybierz: (opcja 'T →' U) → seq <'T> →' U Stosuje daną funkcję do kolejnych elementów, zwracając pierwszą wartość, gdy funkcja zwraca Some wartość.
readonly: seq <'T> → seq <' T> Tworzy nowy obiekt sekwencji, który jest delegowany do danego obiektu sekwencji. Dzięki temu oryginalna sekwencja nie może zostać ponownie odkryta i zmutowana przez rzutowanie typu. Na przykład, jeśli dana jest tablica, zwrócona sekwencja zwróci elementy tablicy, ale nie można rzutować zwróconego obiektu sekwencji na tablicę.
zmniejszyć: ('T →' T → 'T) → seq <' T> → 'T Stosuje funkcję do każdego elementu sekwencji, tworząc wątek argumentu akumulatora przez obliczenia. Zacznij od zastosowania funkcji do pierwszych dwóch elementów. Następnie wprowadź ten wynik do funkcji wraz z trzecim elementem i tak dalej. Zwróć wynik końcowy.
skanowanie: ('Stan →' T → 'Stan) →' Stan → seq <'T> → seq <' Stan> Podobnie jak Seq.fold, ale oblicza na żądanie i zwraca sekwencję wyników pośrednich i końcowych.
singleton: 'T → seq <' T> Zwraca sekwencję, która daje tylko jeden element.
skip: int → seq <'T> → seq <' T> Zwraca sekwencję, która pomija określoną liczbę elementów sekwencji podstawowej, a następnie zwraca pozostałe elementy sekwencji.
skipWhile: ('T → bool) → seq <' T> → seq <'T> Zwraca sekwencję, która po wykonaniu iteracji pomija elementy sekwencji bazowej, gdy dany predykat zwraca true, a następnie zwraca pozostałe elementy sekwencji.
sort: seq <'T> → seq <' T> Podaje sekwencję uporządkowaną według kluczy.
sortBy: ('T →' Key) → seq <'T> → seq <' T> Stosuje funkcję generującą klucz do każdego elementu sekwencji i zwraca sekwencję uporządkowaną według kluczy. Klucze są porównywane przy użyciu porównania ogólnego zaimplementowanego przez Operators.compare.
suma: seq <^ T> → ^ T Zwraca sumę elementów w sekwencji.
sumBy Zwraca sumę wyników wygenerowanych przez zastosowanie funkcji do każdego elementu sekwencji.
take: int → seq <'T> → seq <' T> Zwraca pierwsze elementy sekwencji do określonej liczby.
takeWhile: ('T → bool) → seq <' T> → seq <'T> Zwraca sekwencję, która po wykonaniu iteracji zwraca elementy sekwencji bazowej, podczas gdy dany predykat zwraca true, a następnie nie zwraca żadnych dalszych elementów.
toArray: seq <'T> →' T [] Tworzy tablicę z podanej kolekcji.
toList: seq <'T> →' T list Tworzy listę z podanej kolekcji.
truncate: int → seq <'T> → seq <' T> Zwraca sekwencję, która po wyliczeniu zwraca nie więcej niż określoną liczbę elementów.
tryFind: ('T → bool) → seq <' T> → 'T opcja Zwraca pierwszy element, dla którego zwraca dana funkcja true, lub None jeśli taki element nie istnieje.
tryFindIndex: ('T → bool) → seq <' T> → int opcja Zwraca indeks pierwszego elementu w sekwencji, który spełnia podany predykat lub None jeśli taki element nie istnieje.
tryPick: (opcja 'T →' U) → seq <'T> → opcja' U Stosuje daną funkcję do kolejnych elementów, zwracając pierwszą wartość, gdy funkcja zwraca Some wartość.
unfold: ('Stan →' T * 'opcja stanu) →' Stan → sekwencja <'T> Zwraca sekwencję zawierającą elementy wygenerowane przez dane obliczenie.
gdzie: ('T → bool) → seq <' T> → seq <'T> Zwraca nową kolekcję zawierającą tylko elementy kolekcji, dla których zwraca dany predykat true. Synonim dla Seq.filter.
windowed: int → seq <'T> → seq <' T []> Zwraca sekwencję, która daje przesuwane okna zawierające elementy narysowane z sekwencji wejściowej. Każde okno jest zwracane jako nowa tablica.
zip: seq <'T1> → seq <' T2> → seq <'T1 *' T2> Łączy dwie sekwencje w listę par. Dwie sekwencje nie muszą mieć równych długości - gdy jedna sekwencja jest wyczerpana, wszystkie pozostałe elementy w drugiej sekwencji są ignorowane.
zip3: seq <'T1> → seq <' T2> → seq <'T3> → seq <' T1 * 'T2 *' T3> Łączy trzy sekwencje w listę trójek. Sekwencje nie muszą mieć równych długości - gdy jedna sekwencja jest wyczerpana, wszelkie pozostałe elementy w innych sekwencjach są ignorowane.

Poniższe przykłady pokazują zastosowania niektórych z powyższych funkcji -

Przykład 1

Ten program tworzy pustą sekwencję i wypełnia ją później -

(* Creating sequences *)
let emptySeq = Seq.empty
let seq1 = Seq.singleton 20

printfn"The singleton sequence:"
printfn "%A " seq1
printfn"The init sequence:"

let seq2 = Seq.init 5 (fun n -> n * 3)
Seq.iter (fun i -> printf "%d " i) seq2
printfn""

(* converting an array to sequence by using cast *)
printfn"The array sequence 1:"
let seq3 = [| 1 .. 10 |] :> seq<int>
Seq.iter (fun i -> printf "%d " i) seq3
printfn""

(* converting an array to sequence by using Seq.ofArray *)
printfn"The array sequence 2:"
let seq4 = [| 2..2.. 20 |] |> Seq.ofArray
Seq.iter (fun i -> printf "%d " i) seq4
printfn""

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The singleton sequence:
seq [20]
The init sequence:
0 3 6 9 12
The array sequence 1:
1 2 3 4 5 6 7 8 9 10
The array sequence 2:
2 4 6 8 10 12 14 16 18 20

Należy pamiętać, że -

  • Metoda Seq.empty tworzy pustą sekwencję.

  • Metoda Seq.singleton tworzy sekwencję tylko jednego określonego elementu.

  • Metoda Seq.init tworzy sekwencję, dla której tworzone są elementy przy użyciu danej funkcji.

  • Metody Seq.ofArray i Seq.ofList <'T> tworzą sekwencje z tablic i list.

  • Metoda Seq.iter umożliwia iterację sekwencji.

Przykład 2

Metoda Seq.unfold generuje sekwencję z funkcji obliczeniowej, która przyjmuje stan i przekształca go w celu utworzenia każdego kolejnego elementu w sekwencji.

Poniższa funkcja daje pierwsze 20 liczb naturalnych -

let seq1 = Seq.unfold (fun state -> if (state > 20) then None else Some(state, state + 1)) 0
printfn "The sequence seq1 contains numbers from 0 to 20."
for x in seq1 do printf "%d " x
printfn" "

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The sequence seq1 contains numbers from 0 to 20.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

Przykład 3

Metoda Seq.truncate tworzy sekwencję z innej sekwencji, ale ogranicza sekwencję do określonej liczby elementów.

Metoda Seq.take tworzy nową sekwencję zawierającą określoną liczbę elementów od początku sekwencji.

let mySeq = seq { for i in 1 .. 10 -> 3*i }
let truncatedSeq = Seq.truncate 5 mySeq
let takeSeq = Seq.take 5 mySeq

printfn"The original sequence"
Seq.iter (fun i -> printf "%d " i) mySeq
printfn""

printfn"The truncated sequence"
Seq.iter (fun i -> printf "%d " i) truncatedSeq
printfn""

printfn"The take sequence"
Seq.iter (fun i -> printf "%d " i) takeSeq
printfn""

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The original sequence
3 6 9 12 15 18 21 24 27 30
The truncated sequence
3 6 9 12 15
The take sequence
3 6 9 12 15

Zestaw w F # to struktura danych, która działa jak kolekcja elementów bez zachowania kolejności, w jakiej elementy są wstawiane. Zestawy nie pozwalają na wstawianie do kolekcji zduplikowanych wpisów.

Tworzenie zestawów

Zestawy można tworzyć w następujący sposób -

  • Tworząc pusty zestaw za pomocą Set.empty i dodając elementy za pomocą funkcji add.
  • Przekształcanie sekwencji i list w zestawy.

Poniższy program demonstruje techniki -

(* creating sets *)
let set1 = Set.empty.Add(3).Add(5).Add(7). Add(9)
printfn"The new set: %A" set1

let weekdays = Set.ofList ["mon"; "tues"; "wed"; "thurs"; "fri"]
printfn "The list set: %A" weekdays

let set2 = Set.ofSeq [ 1 .. 2.. 10 ]
printfn "The sequence set: %A" set2

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

The new set: set [3; 5; 7; 9]
The list set: set ["fri"; "mon"; "thurs"; "tues"; "wed"]
The sequence set: set [1; 3; 5; 7; 9]

Podstawowe operacje na zbiorach

Poniższa tabela przedstawia podstawowe operacje na zestawach -

Wartość Opis
dodaj: 'T → Ustaw <' T> → Ustaw <'T> Zwraca nowy zestaw z elementem dodanym do zestawu. Żaden wyjątek nie jest zgłaszany, jeśli zestaw zawiera już dany element.
zawiera: 'T → Ustaw <' T> → bool Ocenia do true czy dany element należy do danego zbioru.
count: Ustaw <'T> → int Zwraca liczbę elementów w zestawie.
różnica: Ustaw <'T> → Ustaw <' T> → Ustaw <'T> Zwraca nowy zestaw z elementami drugiego zestawu usuniętymi z pierwszego.
pusty: Ustaw <'T> Pusty zestaw dla określonego typu.
istnieje: ('T → bool) → Set <' T> → bool Sprawdza, czy którykolwiek element kolekcji spełnia podany predykat. Jeśli funkcja wejściowa jest predykatem, a elementy są i0 ... iN, to ta funkcja oblicza predykat i0 lub ... lub predykat iN.
filtr: ('T → bool) → Ustaw <' T> → Ustaw <'T> Zwraca nową kolekcję zawierającą tylko elementy kolekcji, dla których zwraca dany predykat true.
fold: ('Stan →' T → 'Stan) →' Stan → Ustaw <'T> →' Stan Stosuje daną funkcję sumującą do wszystkich elementów zbioru.
foldBack: ('T →' Stan → 'Stan) → Ustaw <' T> → 'Stan →' Stan Stosuje daną funkcję sumującą do wszystkich elementów zbioru.
forall: ('T → bool) → Set <' T> → bool Sprawdza, czy wszystkie elementy kolekcji spełniają podany predykat. Jeśli funkcją wejściową jest p, a elementy to i0 ... iN, to ta funkcja oblicza p i0 && ... && p iN.
przecięcie: Ustaw <'T> → Ustaw <' T> → Ustaw <'T> Oblicza przecięcie dwóch zbiorów.
intersectMany: seq <Ustaw <'T >> → Ustaw <' T> Oblicza przecięcie sekwencji zbiorów. Sekwencja nie może być pusta.
isEmpty: Set <'T> → bool Zwroty true jeśli zestaw jest pusty.
isProperSubset: Set <'T> → Set <' T> → bool Ocenia do true jeśli wszystkie elementy pierwszego zestawu są w drugim, a co najmniej jeden element drugiego nie znajduje się w pierwszym.
isProperSuperset: Set <'T> → Set <' T> → bool Ocenia do true jeśli wszystkie elementy drugiego zestawu znajdują się w pierwszym, a przynajmniej jeden element pierwszego nie znajduje się w drugim.
isSubset: Set <'T> → Set <' T> → bool Ocenia do true jeśli wszystkie elementy pierwszego zestawu znajdują się w drugim.
isSuperset: Set <'T> → Set <' T> → bool Ocenia do true jeśli wszystkie elementy drugiego zestawu znajdują się w pierwszym.
iter: ('T → jednostka) → Ustaw <' T> → jednostka Stosuje daną funkcję do każdego elementu zestawu, zgodnie z funkcją porównania.
map: ('T →' U) → Ustaw <'T> → Ustaw <' U> Zwraca nową kolekcję zawierającą wyniki zastosowania danej funkcji do każdego elementu zestawu wejściowego.
maxElement: Ustaw <'T> →' T Zwraca najwyższy element w zestawie zgodnie z kolejnością używaną w zestawie.
minElement: Ustaw <'T> →' T Zwraca najniższy element w zestawie zgodnie z kolejnością używaną dla zestawu.
ofArray: 'Tablica T → Ustaw <' T> Tworzy zestaw zawierający te same elementy, co dana tablica.
ofList: 'T lista → Ustaw <' T> Tworzy zestaw zawierający te same elementy, co podana lista.
ofSeq: seq <'T> → Set <' T> Tworzy nową kolekcję z danego wyliczalnego obiektu.
partycja: ('T → bool) → Ustaw <' T> → Ustaw <'T> * Ustaw <' T> Dzieli zestaw na dwa zestawy zawierające elementy, dla których dany predykat zwraca odpowiednio prawdę i fałsz.
usuń: 'T → Ustaw <' T> → Ustaw <'T> Zwraca nowy zestaw z usuniętym danym elementem. Żaden wyjątek nie jest zgłaszany, jeśli zestaw nie zawiera danego elementu.
singleton: 'T → Ustaw <' T> Zestaw zawierający dany element.
toArray: Ustaw tablicę <'T> →' T. Tworzy tablicę zawierającą elementy zestawu w kolejności.
toList: Ustaw <'T> →' T list Tworzy listę zawierającą elementy zestawu w kolejności.
toSeq: Ustaw <'T> → seq <' T> Zwraca uporządkowany widok kolekcji jako wyliczalny obiekt.
suma: Ustaw <'T> → Ustaw <' T> → Ustaw <'T> Oblicza sumę dwóch zbiorów.
unionMany: seq <Ustaw <'T >> → Ustaw <' T> Oblicza sumę sekwencji zbiorów.

Poniższy przykład demonstruje zastosowania niektórych z powyższych funkcji -

Przykład

let a = Set.ofSeq [ 1 ..2.. 20 ]
let b = Set.ofSeq [ 1 ..3 .. 20 ]
let c = Set.intersect a b
let d = Set.union a b
let e = Set.difference a b

printfn "Set a: "
Set.iter (fun x -> printf "%O " x) a
printfn""

printfn "Set b: "
Set.iter (fun x -> printf "%O " x) b
printfn""

printfn "Set c = set intersect of a and b : "
Set.iter (fun x -> printf "%O " x) c
printfn""

printfn "Set d = set union of a and b : "
Set.iter (fun x -> printf "%O " x) d
printfn""

printfn "Set e = set difference of a and b : "
Set.iter (fun x -> printf "%O " x) e
printfn""

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Set a:
1 3 5 7 9 11 13 15 17 19
Set b:
1 4 7 10 13 16 19
Set c = set intersect of a and b :
1 7 13 19
Set d = set union of a and b :
1 3 4 5 7 9 10 11 13 15 16 17 19
Set e = set difference of a and b :
3 5 9 11 15 17

W języku F # mapa to specjalny rodzaj zestawu, który kojarzy wartości z kluczem. Mapa jest tworzona w podobny sposób jak zestawy.

Tworzenie map

Mapy tworzy się, tworząc pustą mapę za pomocą Map.empty i dodając elementy za pomocą funkcji Dodaj. Poniższy przykład demonstruje to -

Przykład

(* Create an empty Map *)
let students =
   Map.empty. (* Creating an empty Map *)
      Add("Zara Ali", "1501").
      Add("Rishita Gupta", "1502").
      Add("Robin Sahoo", "1503").
      Add("Gillian Megan", "1504");;
printfn "Map - students: %A" students

(* Convert a list to Map *)
let capitals =
   [ "Argentina", "Buenos Aires";
      "France ", "Paris";
      "Chili", "Santiago";
      "Malaysia", " Kuala Lumpur";
      "Switzerland", "Bern" ]
   |> Map.ofList;;
printfn "Map capitals : %A" capitals

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Map - students: map
[("Gillian Megan", "1504"); ("Rishita Gupta", "1502"); ("Robin Sahoo", "1503
");
("Zara Ali", "1501")]
Map capitals : map
[("Argentina", "Buenos Aires"); ("Chili", "Santiago"); ("France ", "Paris");
("Malaysia", " Kuala Lumpur"); ("Switzerland", "Bern")]

Możesz uzyskać dostęp do poszczególnych elementów mapy za pomocą klawisza.

Przykład

(* Create an empty Map *)
let students =
   Map.empty. (* Creating an empty Map *)
      Add("Zara Ali", "1501").
      Add("Rishita Gupta", "1502").
      Add("Robin Sahoo", "1503").
      Add("Gillian Megan", "1504");;
printfn "Map - students: %A" students

(*Accessing an element using key *)
printfn "%A" students.["Zara Ali"]

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Map - students: map
[("Gillian Megan", "1504"); ("Rishita Gupta", "1502"); ("Robin Sahoo", "1503
");
("Zara Ali", "1501")]
"1501"

Podstawowe operacje na mapach

Dodaj nazwę modułu

Poniższa tabela przedstawia podstawowe operacje na mapach -

Członek Opis
Dodaj Zwraca nową mapę z powiązaniem dodanym do danej mapy.
ContainsKey Testuje, czy element znajduje się w domenie mapy.
Liczyć Liczba powiązań na mapie.
Jest pusty Zwraca wartość true, jeśli na mapie nie ma żadnych powiązań.
Pozycja Wyszukaj element na mapie. Podnosi KeyNotFoundException, jeśli w mapie nie istnieje żadne powiązanie.
Usunąć Usuwa element z domeny mapy. Żaden wyjątek nie jest zgłaszany, jeśli element nie jest obecny.
TryFind Wyszukaj element na mapie, zwracając plik Some wartość, jeśli element znajduje się w domenie mapy i None Jeśli nie.

Poniższy przykład demonstruje zastosowania niektórych z powyższych funkcji -

Przykład

(* Create an empty Map *)
let students =
   Map.empty. (* Creating an empty Map *)
      Add("Zara Ali", "1501").
      Add("Rishita Gupta", "1502").
      Add("Robin Sahoo", "1503").
      Add("Gillian Megan", "1504").
      Add("Shraddha Dubey", "1505").
      Add("Novonil Sarker", "1506").
      Add("Joan Paul", "1507");;
printfn "Map - students: %A" students
printfn "Map - number of students: %d" students.Count

(* finding the registration number of a student*)
let found = students.TryFind "Rishita Gupta"
match found with
| Some x -> printfn "Found %s." x
| None -> printfn "Did not find the specified value."

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Map - students: map
[("Gillian Megan", "1504"); ("Joan Paul", "1507"); ("Novonil Sarker", "1506"
);
("Rishita Gupta", "1502"); ("Robin Sahoo", "1503");
("Shraddha Dubey", "1505"); ("Zara Ali", "1501")]
Map - number of students: 7
Found 1502.

Związki, czyli związki rozłączne, umożliwiają tworzenie złożonych struktur danych reprezentujących dobrze zdefiniowany zestaw wyborów. Na przykład musisz zbudować implementację zmiennej wyboru , która ma dwie wartości tak i nie. Za pomocą narzędzia Związki możesz to zaprojektować.

Składnia

Związki rozłączne są definiowane przy użyciu następującej składni -

type type-name =
   | case-identifier1 [of [ fieldname1 : ] type1 [ * [ fieldname2 : ] 
type2 ...]
   | case-identifier2 [of [fieldname3 : ]type3 [ * [ fieldname4 : ]type4 ...]
...

Nasza prosta implementacja, wybór, będzie wyglądać następująco -

type choice =
   | Yes
   | No

W poniższym przykładzie zastosowano wybór typu -

type choice =
   | Yes
   | No

let x = Yes (* creates an instance of choice *)
let y = No (* creates another instance of choice *)
let main() =
   printfn "x: %A" x
   printfn "y: %A" y
main()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

x: Yes
y: No

Przykład 1

Poniższy przykład pokazuje implementację stanów napięcia, które ustawiają się nieco na wysokie lub niskie -

type VoltageState =
   | High
   | Low

let toggleSwitch = function (* pattern matching input *)
   | High -> Low
   | Low -> High

let main() =
   let on = High
   let off = Low
   let change = toggleSwitch off

   printfn "Switch on state: %A" on
   printfn "Switch off state: %A" off
   printfn "Toggle off: %A" change
   printfn "Toggle the Changed state: %A" (toggleSwitch change)

main()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Switch on state: High
Switch off state: Low
Toggle off: High
Toggle the Changed state: Low

Przykład 2

type Shape =
   // here we store the radius of a circle
   | Circle of float

   // here we store the side length.
   | Square of float

   // here we store the height and width.
   | Rectangle of float * float

let pi = 3.141592654

let area myShape =
   match myShape with
   | Circle radius -> pi * radius * radius
   | Square s -> s * s
   | Rectangle (h, w) -> h * w

let radius = 12.0
let myCircle = Circle(radius)
printfn "Area of circle with radius %g: %g" radius (area myCircle)

let side = 15.0
let mySquare = Square(side)
printfn "Area of square that has side %g: %g" side (area mySquare)

let height, width = 5.0, 8.0
let myRectangle = Rectangle(height, width)
printfn "Area of rectangle with height %g and width %g is %g" height width (area myRectangle)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Area of circle with radius 12: 452.389
Area of square that has side 15: 225
Area of rectangle with height 5 and width 8 is 40

Zmienne w języku F # to immutable,co oznacza, że ​​po przypisaniu zmiennej do wartości nie można jej zmienić. W rzeczywistości są one kompilowane jako statyczne właściwości tylko do odczytu.

Poniższy przykład ilustruje to.

Przykład

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

Podczas kompilowania i wykonywania programu wyświetla następujący komunikat o błędzie -

Duplicate definition of value 'x'
Duplicate definition of value 'Y'
Duplicate definition of value 'Z'

Zmienne zmienne

Czasami trzeba zmienić wartości przechowywane w zmiennej. Aby określić, że może nastąpić zmiana wartości zadeklarowanej i przypisanej zmiennej w dalszej części programu, F # udostępniamutablesłowo kluczowe. Za pomocą tego słowa kluczowego możesz deklarować i przypisywać zmienne zmienne, których wartości będziesz zmieniać.

Plik mutable słowo kluczowe pozwala zadeklarować i przypisać wartości w zmiennej zmiennej.

Możesz przypisać jakąś wartość początkową do zmiennej zmiennej za pomocą letsłowo kluczowe. Jednak aby przypisać do niego nową kolejną wartość, musisz użyć rozszerzenia<- operator.

Na przykład,

let mutable x = 10
x <- 15

Poniższy przykład wyjaśni tę koncepcję -

Przykład

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

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

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

Zastosowania zmiennych danych

Zmienne dane są często wymagane i wykorzystywane w przetwarzaniu danych, szczególnie w przypadku struktury danych rekordów. Poniższy przykład demonstruje to -

open System

type studentData =
   { ID : int;
      mutable IsRegistered : bool;
      mutable RegisteredText : string; }

let getStudent id =
   { ID = id;
      IsRegistered = false;
      RegisteredText = null; }

let registerStudents (students : studentData list) =
   students |> List.iter(fun st ->
      st.IsRegistered <- true
      st.RegisteredText <- sprintf "Registered %s" (DateTime.Now.ToString("hh:mm:ss"))

      Threading.Thread.Sleep(1000) (* Putting thread to sleep for 1 second to simulate processing overhead. *))

let printData (students : studentData list) =
   students |> List.iter (fun x -> printfn "%A" x)

let main() =
   let students = List.init 3 getStudent

   printfn "Before Process:"
   printData students

   printfn "After process:"
   registerStudents students
   printData students

   Console.ReadKey(true) |> ignore

main()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Before Process:
{ID = 0;
IsRegistered = false;
RegisteredText = null;}
{ID = 1;
IsRegistered = false;
RegisteredText = null;}
{ID = 2;
IsRegistered = false;
RegisteredText = null;}
After process:
{ID = 0;
IsRegistered = true;
RegisteredText = "Registered 05:39:15";}
{ID = 1;
IsRegistered = true;
RegisteredText = "Registered 05:39:16";}
{ID = 2;
IsRegistered = true;
RegisteredText = "Registered 05:39:17";}

Tablice to zmienne kolekcje o stałym rozmiarze, liczone od zera, kolejnych elementów danych, które są tego samego typu.

Tworzenie tablic

Możesz tworzyć tablice przy użyciu różnych składni i sposobów lub używając funkcji z modułu Array. W tej sekcji omówimy tworzenie tablic bez korzystania z funkcji modułu.

Istnieją trzy syntaktyczne sposoby tworzenia tablic bez funkcji -

  • Wymieniając kolejne wartości między [| i |] i oddzielone średnikami.
  • Umieszczając każdy element w osobnym wierszu, w takim przypadku separator średnika jest opcjonalny.
  • Używając wyrażeń sekwencyjnych.

Dostęp do elementów tablicy można uzyskać za pomocą operatora kropki (.) I nawiasów kwadratowych ([i]).

Poniższy przykład demonstruje tworzenie tablic -

//using semicolon separator
let array1 = [| 1; 2; 3; 4; 5; 6 |]
for i in 0 .. array1.Length - 1 do
   printf "%d " array1.[i]
printfn" "

// without semicolon separator
let array2 =
   [|
      1
      2
      3
      4
      5
   |]
for i in 0 .. array2.Length - 1 do
   printf "%d " array2.[i]
printfn" "

//using sequence
let array3 = [| for i in 1 .. 10 -> i * i |]
for i in 0 .. array3.Length - 1 do
   printf "%d " array3.[i]
printfn" "

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

1 2 3 4 5 6
1 2 3 4 5
1 4 9 16 25 36 49 64 81 100

Podstawowe operacje na tablicach

Moduł biblioteki Microsoft.FSharp.Collections.Array obsługuje operacje na tablicach jednowymiarowych.

W poniższej tabeli przedstawiono podstawowe operacje na tablicach -

Wartość Opis
dołącz: „T [] →„ T [] → „T []” Tworzy tablicę zawierającą elementy jednej tablicy, po których następują elementy innej tablicy.
średnia: ^ T [] → ^ T Zwraca średnią elementów w tablicy.
średnia Przez: ('T → ^ U) →' T [] → ^ U Zwraca średnią z elementów wygenerowanych przez zastosowanie funkcji do każdego elementu tablicy.
blit: 'T [] → int →' T [] → int → int → unit Odczytuje zakres elementów z jednej tablicy i zapisuje je w innej.
wybierz: (opcja 'T → U) →' T [] → 'U [] Stosuje podaną funkcję do każdego elementu tablicy. Zwraca tablicę zawierającą wyniki x dla każdego elementu, dla którego funkcja zwraca Some (x).
zbierać: ('T →' U []) → T [] → 'U [] Stosuje podaną funkcję do każdego elementu tablicy, konkatenuje wyniki i zwraca połączoną tablicę.
concat: seq <'T []> →' T [] Tworzy tablicę zawierającą elementy każdej z podanych sekwencji tablic.
kopia: „T →” T [] Tworzy tablicę zawierającą elementy podanej tablicy.
utwórz: int → 'T →' T [] Tworzy tablicę, której wszystkie elementy są początkowo podaną wartością.
pusty: „T [] Zwraca pustą tablicę podanego typu.
istnieje: ('T → bool) →' T [] → bool Testuje, czy dowolny element tablicy spełnia podany predykat.
istnieje2: ('T1 →' T2 → bool) → 'T1 [] →' T2 [] → bool Testuje, czy dowolna para odpowiednich elementów dwóch tablic spełnia podany warunek.
wypełnienie: 'T [] → int → int →' T → unit Wypełnia zakres elementów tablicy podaną wartością.
filtr: ('T → bool) →' T [] → 'T [] Zwraca kolekcję zawierającą tylko elementy podanej tablicy, dla których zwraca podany warunek true.
znajdź: ('T → bool) →' T [] → 'T Zwraca pierwszy element, dla którego podana funkcja zwraca true. Podnosi KeyNotFoundException, jeśli taki element nie istnieje.
findIndex: ('T → bool) →' T [] → int Zwraca indeks pierwszego elementu w tablicy, który spełnia podany warunek. Podnosi KeyNotFoundException, jeśli żaden z elementów nie spełnia warunku.
fold: („Stan →„ T → ”Stan) →„ Stan → „T [] →„ Stan Stosuje funkcję do każdego elementu tablicy, tworząc wątek argumentu akumulatora przez obliczenia. Jeśli funkcją wejściową jest f, a elementy tablicy to i0 ... iN, ta funkcja oblicza f (... (fs i0) ...) iN.
fold2: ('Stan →' T1 → 'T2 →' Stan) → 'Stan →' T1 [] → 'T2 [] →' Stan Stosuje funkcję do par elementów z dwóch dostarczonych tablic, od lewej do prawej, tworząc wątek argumentu akumulatora przez obliczenia. Dwie tablice wejściowe muszą mieć takie same długości; w przeciwnym razie zostanie zgłoszony ArgumentException.
foldBack: ('T →' Stan → 'Stan) →' T [] → 'Stan →' Stan Stosuje funkcję do każdego elementu tablicy, tworząc wątek argumentu akumulatora przez obliczenia. Jeśli funkcją wejściową jest f, a elementy tablicy to i0 ... iN, ta funkcja oblicza f i0 (... (f iN s)).
foldBack2 : ('T1 → 'T2 → 'State → 'State) → 'T1 [] → 'T2 [] → 'State → 'State Applies a function to pairs of elements from two supplied arrays, right-to-left, threading an accumulator argument through the computation. The two input arrays must have the same lengths; otherwise, ArgumentException is raised.
forall : ('T → bool) → 'T [] → bool Tests whether all elements of an array satisfy the supplied condition.
forall2 : ('T1 → 'T2 → bool) → 'T1 [] → 'T2 [] → bool Tests whether all corresponding elements of two supplied arrays satisfy a supplied condition.
get : 'T [] → int → 'T Gets an element from an array.
init : int → (int → 'T) → 'T [] Uses a supplied function to create an array of the supplied dimension.
isEmpty : 'T [] → bool Tests whether an array has any elements.
iter : ('T → unit) → 'T [] → unit Applies the supplied function to each element of an array.
iter2 : ('T1 → 'T2 → unit) → 'T1 [] → 'T2 [] → unit) Applies the supplied function to a pair of elements from matching indexes in two arrays. The two arrays must have the same lengths; otherwise, ArgumentException is raised.
iteri : (int → 'T → unit) → 'T [] → unit Applies the supplied function to each element of an array. The integer passed to the function indicates the index of the element.
iteri2 : (int → 'T1 → 'T2 → unit) → 'T1 [] → 'T2 [] → unit Applies the supplied function to a pair of elements from matching indexes in two arrays, also passing the index of the elements. The two arrays must have the same lengths; otherwise, an ArgumentException is raised.
length : 'T [] → int Returns the length of an array. The Length property does the same thing.
map : ('T → 'U) → 'T [] → 'U [] Creates an array whose elements are the results of applying the supplied function to each of the elements of a supplied array.
map2 : ('T1 → 'T2 → 'U) → 'T1 [] → 'T2 [] → 'U [] Creates an array whose elements are the results of applying the supplied function to the corresponding elements of two supplied arrays. The two input arrays must have the same lengths; otherwise, ArgumentException is raised.
mapi : (int → 'T → 'U) → 'T [] → 'U [] Creates an array whose elements are the results of applying the supplied function to each of the elements of a supplied array. An integer index passed to the function indicates the index of the element being transformed.
mapi2 : (int → 'T1 → 'T2 → 'U) → 'T1 [] → 'T2 [] → 'U [] Creates an array whose elements are the results of applying the supplied function to the corresponding elements of the two collections pairwise, also passing the index of the elements. The two input arrays must have the same lengths; otherwise, ArgumentException is raised.
max : 'T [] → 'T Returns the largest of all elements of an array. Operators.max is used to compare the elements.
maxBy : ('T → 'U) → 'T [] → 'T Returns the largest of all elements of an array, compared via Operators.max on the function result.
min : ('T [] → 'T Returns the smallest of all elements of an array. Operators.min is used to compare the elements.
minBy : ('T → 'U) → 'T [] → 'T Returns the smallest of all elements of an array. Operators.min is used to compare the elements.
ofList : 'T list → 'T [] Creates an array from the supplied list.
ofSeq : seq<'T> → 'T [] Creates an array from the supplied enumerable object.
partition : ('T → bool) → 'T [] → 'T [] * 'T [] Splits an array into two arrays, one containing the elements for which the supplied condition returns true, and the other containing those for which it returns false.
permute : (int → int) → 'T [] → 'T [] Permutes the elements of an array according to the specified permutation.
pick : ('T → 'U option) → 'T [] → 'U Applies the supplied function to successive elements of a supplied array, returning the first result where the function returns Some(x) for some x. If the function never returns Some(x), KeyNotFoundException is raised.
reduce : ('T → 'T → 'T) → 'T [] → 'T Applies a function to each element of an array, threading an accumulator argument through the computation. If the input function is f and the array elements are i0...iN, this function computes f (...(f i0 i1)...) iN. If the array has size zero, ArgumentException is raised.
reduceBack : ('T → 'T → 'T) → 'T [] → 'T Applies a function to each element of an array, threading an accumulator argument through the computation. If the input function is f and the elements are i0...iN, this function computes f i0 (...(f iN-1 iN)). If the array has size zero, ArgumentException is raised.
rev : 'T [] → 'T [] Reverses the order of the elements in a supplied array.
scan : ('State → 'T → 'State) → 'State → 'T [] → 'State []) Behaves like fold, but returns the intermediate results together with the final results.
scanBack : ('T → 'State → 'State) → 'T [] → 'State → 'State [] Behaves like foldBack, but returns the intermediary results together with the final results.
set : 'T [] → int → 'T → unit Sets an element of an array.
sort : 'T[] → 'T [] Sorts the elements of an array and returns a new array. Operators.compare is used to compare the elements.
sortBy : ('T → 'Key) → 'T [] → 'T [] Sorts the elements of an array by using the supplied function to transform the elements to the type on which the sort operation is based, and returns a new array. Operators.compare is used to compare the elements.
sortInPlace : 'T [] → unit Sorts the elements of an array by changing the array in place, using the supplied comparison function. Operators.compare is used to compare the elements.
sortInPlaceBy : ('T → 'Key) → 'T [] → unit Sorts the elements of an array by changing the array in place, using the supplied projection for the keys. Operators.compare is used to compare the elements.
sortInPlaceWith : ('T → 'T → int) → 'T [] → unit Sorts the elements of an array by using the supplied comparison function to change the array in place.
sortWith : ('T → 'T → int) → 'T [] → 'T [] Sorts the elements of an array by using the supplied comparison function, and returns a new array.
sub : 'T [] → int → int → 'T [] Creates an array that contains the supplied subrange, which is specified by starting index and length.
sum : 'T [] → ^T Returns the sum of the elements in the array.
sumBy : ('T → ^U) → 'T [] → ^U Returns the sum of the results generated by applying a function to each element of an array.
toList : 'T [] → 'T list Converts the supplied array to a list.
toSeq : 'T [] → seq<'T> Views the supplied array as a sequence.
tryFind : ('T → bool) → 'T [] → 'T option Returns the first element in the supplied array for which the supplied function returns true. Returns None if no such element exists.
tryFindIndex : ('T → bool) → 'T [] → int option Returns the index of the first element in an array that satisfies the supplied condition.
tryPick : ('T → 'U option) → 'T [] → 'U option Applies the supplied function to successive elements of the supplied array, and returns the first result where the function returns Some(x) for some x. If the function never returns Some(x), None is returned.
unzip : ('T1 * 'T2) [] → 'T1 [] * 'T2 [] Splits an array of tuple pairs into a tuple of two arrays.
unzip3 : ('T1 * 'T2 * 'T3) [] → 'T1 [] * 'T2 [] * 'T3 [] Splits an array of tuples of three elements into a tuple of three arrays.
zeroCreate : int → 'T [] Creates an array whose elements are initially set to the default value Unchecked.defaultof<'T>.
zip : 'T1 [] → 'T2 [] → ('T1 * 'T2) [] Combines two arrays into an array of tuples that have two elements. The two arrays must have equal lengths; otherwise, ArgumentException is raised.
zip3 : 'T1 [] → 'T2 [] → 'T3 [] → ('T1 * 'T2 * 113 'T3) [] Combines three arrays into an array of tuples that have three elements. The three arrays must have equal lengths; otherwise, ArgumentException is raised.

In the following section, we will see the uses of some of these functionalities.

Creating Arrays Using Functions

The Array module provides several functions that create an array from scratch.

  • The Array.empty function creates a new empty array.

  • The Array.create function creates an array of a specified size and sets all the elements to given values.

  • The Array.init function creates an array, given a dimension and a function to generate the elements.

  • The Array.zeroCreate function creates an array in which all the elements are initialized to the zero value.

  • The Array.copy function creates a new array that contains elements that are copied from an existing array.

  • The Array.sub function generates a new array from a subrange of an array.

  • The Array.append function creates a new array by combining two existing arrays.

  • The Array.choose function selects elements of an array to include in a new array.

  • The Array.collect function runs a specified function on each array element of an existing array and then collects the elements generated by the function and combines them into a new array.

  • The Array.concat function takes a sequence of arrays and combines them into a single array.

  • The Array.filter function takes a Boolean condition function and generates a new array that contains only those elements from the input array for which the condition is true.

  • The Array.rev function generates a new array by reversing the order of an existing array.

The following examples demonstrate these functions −

Example 1

(* using create and set *)
let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
   Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
   printf "%s " (Array.get array1 i)
printfn " "

(* empty array *)
let array2 = Array.empty
printfn "Length of empty array: %d" array2.Length

let array3 = Array.create 10 7.0
printfn "Float Array: %A" array3

(* using the init and zeroCreate *)
let array4 = Array.init 10 (fun index -> index * index)
printfn "Array of squares: %A" array4

let array5 : float array = Array.zeroCreate 10
let (myZeroArray : float array) = Array.zeroCreate 10
printfn "Float Array: %A" array5

When you compile and execute the program, it yields the following output −

0 1 2 3 4 5 6 7 8 9
Length of empty array: 0
Float Array: [|7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]
Float Array: [|0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0|]

Example 2

(* creating subarray from element 5 *)
(* containing 15 elements thereon *)

let array1 = [| 0 .. 50 |]
let array2 = Array.sub array1 5 15
printfn "Sub Array:"
printfn "%A" array2

(* appending two arrays *)
let array3 = [| 1; 2; 3; 4|]
let array4 = [| 5 .. 9 |]
printfn "Appended Array:"
let array5 = Array.append array3 array4
printfn "%A" array5

(* using the Choose function *)
let array6 = [| 1 .. 20 |]
let array7 = Array.choose (fun elem -> if elem % 3 = 0 then
                                             Some(float (elem))
                                          else
                                             None) array6
printfn "Array with Chosen elements:"
printfn "%A" array7

(*using the Collect function *)
let array8 = [| 2 .. 5 |]
let array9 = Array.collect (fun elem -> [| 0 .. elem - 1 |]) array8
printfn "Array with collected elements:"
printfn "%A" array9

When you compile and execute the program, it yields the following output −

Sub Array:
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19|]
Appended Array:
[|1; 2; 3; 4; 5; 6; 7; 8; 9|]
Array with Chosen elements:
[|3.0; 6.0; 9.0; 12.0; 15.0; 18.0|]
Array with collected elements:
[|0; 1; 0; 1; 2; 0; 1; 2; 3; 0; 1; 2; 3; 4|]

Searching Arrays

The Array.find function takes a Boolean function and returns the first element for which the function returns true, else raises a KeyNotFoundException.

The Array.findIndex function works similarly except that it returns the index of the element instead of the element itself.

The following example demonstrates this.

Microsoft provides this interesting program example, which finds the first element in the range of a given number that is both a perfect square as well as a perfect cube −

let array1 = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
   let y = sqrt (float x)
   abs(y - round y) < delta

let isPerfectCube (x:int) =
   let y = System.Math.Pow(float x, 1.0/3.0)
   abs(y - round y) < delta

let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1

let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1

printfn "The first element that is both a square and a cube is %d and its index is %d." element index

When you compile and execute the program, it yields the following output −

The first element that is both a square and a cube is 64 and its index is 62.

The List<'T> class represents a strongly typed list of objects that can be accessed by index.

It is a mutable counterpart of the List class. It is similar to arrays, as it can be accessed by an index, however, unlike arrays, lists can be resized. Therefore you need not specify a size during declaration.

Creating a Mutable List

Lists are created using the new keyword and calling the list's constructor. The following example demonstrates this −

(* Creating a List *)
open System.Collections.Generic

let booksList = new List<string>()
booksList.Add("Gone with the Wind")
booksList.Add("Atlas Shrugged")
booksList.Add("Fountainhead")
booksList.Add("Thornbirds")
booksList.Add("Rebecca")
booksList.Add("Narnia")

booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])

When you compile and execute the program, it yields the following output −

0: Gone with the Wind
1: Atlas Shrugged
2: Fountainhead
3: Thornbirds
4: Rebecca
5: Narnia

The List(T) Class

The List(T) class represents a strongly typed list of objects that can be accessed by index. It provide methods to search, sort, and manipulate lists.

The following tables provide the properties, constructors and the methods of the List(T) class −

Properties

Property Description
Capacity Gets or sets the total number of elements the internal data structure can hold without resizing.
Count Gets the number of elements contained in the List(T).
Item Gets or sets the element at the specified index.

Constructors

Constructor Description
List(T)() Initializes a new instance of the List(T) class that is empty and has the default initial capacity.
List(T)(IEnumerable(T)) Initializes a new instance of the List(T) class that contains elements copied from the specified collection and has sufficient capacity to accommodate the number of elements copied.
List(T)(Int32) Initializes a new instance of the List(T) class that is empty and has the specified initial capacity.

Method

Methods Description
Add Adds an object to the end of the List(T).
AddRange Adds the elements of the specified collection to the end of the List(T).
AsReadOnly Returns a read-only IList(T) wrapper for the current collection.
BinarySearch(T) Searches the entire sorted List(T) for an element using the default comparer and returns the zero-based index of the element.
BinarySearch(T, IComparer(T)) Searches the entire sorted List(T) for an element using the specified comparer and returns the zero-based index of the element.
BinarySearch(Int32, Int32, T, IComparer(T)) Searches a range of elements in the sorted List(T) for an element using the specified comparer and returns the zero-based index of the element.
Clear Removes all elements from the List(T).
Contains Determines whether an element is in the List(T).
ConvertAll(TOutput) Converts the elements in the current List(T) to another type, and returns a list containing the converted elements.
CopyTo(T[]) Copies the entire List(T) to a compatible one-dimensional array, starting at the beginning of the target array.
CopyTo(T[], Int32) Copies the entire List(T) to a compatible one-dimensional array, starting at the specified index of the target array.
CopyTo(Int32, T[], Int32, Int32) Copies a range of elements from the List(T) to a compatible one-dimensional array, starting at the specified index of the target array.
Equals(Object) Determines whether the specified object is equal to the current object. (Inherited from Object.)
Exists Determines whether the List(T) contains elements that match the conditions defined by the specified predicate.
Finalize Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection (Inherited from Object).
Find Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire List(T).
FindAll Retrieves all the elements that match the conditions defined by the specified predicate.
FindIndex(Predicate(T)) Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the entire List(T).
FindIndex(Int32, Predicate(T)) Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the List(T) that extends from the specified index to the last element.
FindIndex(Int32, Int32, Predicate(T)) Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the List(T) that starts at the specified index and contains the specified number of elements.
FindLast Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire List(T).
FindLastIndex(Predicate(T)) Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire List(T).
FindLastIndex(Int32, Predicate(T)) Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the List(T) that extends from the first element to the specified index.
FindLastIndex(Int32, Int32, Predicate(T)) Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the List(T) that contains the specified number of elements and ends at the specified index.
ForEach Performs the specified action on each element of the List(T).
GetEnumerator Returns an enumerator that iterates through the List(T).
GetHashCode Serves as the default hash function. (Inherited from Object.)
GetRange Creates a shallow copy of a range of elements in the source List(T).
GetType Gets the Type of the current instance. (Inherited from Object.)
IndexOf(T) Searches for the specified object and returns the zero-based index of the first occurrence within the entire List(T).
IndexOf(T, Int32) Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the List(T) that extends from the specified index to the last element.
IndexOf(T, Int32, Int32) Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the List(T) that starts at the specified index and contains the specified number of elements.
Insert Inserts an element into the List(T) at the specified index.
InsertRange Inserts the elements of a collection into the List(T) at the specified index.
LastIndexOf(T) Searches for the specified object and returns the zero-based index of the last occurrence within the entire List(T).
LastIndexOf(T, Int32) Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the List(T) that extends from the first element to the specified index.
LastIndexOf(T, Int32, Int32) Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the List(T) that contains the specified number of elements and ends at the specified index.
MemberwiseClone Creates a shallow copy of the current Object. (Inherited from Object.)
Remove Removes the first occurrence of a specific object from the List(T).
RemoveAll Removes all the elements that match the conditions defined by the specified predicate.
RemoveAt Removes the element at the specified index of the List(T).
RemoveRange Removes a range of elements from the List(T).
Reverse() Reverses the order of the elements in the entire List(T).
Reverse(Int32, Int32) Reverses the order of the elements in the specified range.
Sort() Sorts the elements in the entire List(T) using the default comparer.
Sort(Comparison(T)) Sorts the elements in the entire List(T) using the specified System. Comparison(T).
Sort(IComparer(T)) Sorts the elements in the entire List(T) using the specified comparer.
Sort(Int32, Int32, IComparer(T)) Sorts the elements in a range of elements in List(T) using the specified comparer.
ToArray Copies the elements of the List(T) to a new array.
ToString Returns a string that represents the current object. (Inherited from Object.)
TrimExcess Sets the capacity to the actual number of elements in the List(T), if that number is less than a threshold value.
TrueForAll Determines whether every element in the List(T) matches the conditions defined by the specified predicate.

Przykład

(* Creating a List *)
open System.Collections.Generic

let booksList = new List<string>()
booksList.Add("Gone with the Wind")
booksList.Add("Atlas Shrugged")
booksList.Add("Fountainhead")
booksList.Add("Thornbirds")
booksList.Add("Rebecca")
booksList.Add("Narnia")

printfn"Total %d books" booksList.Count
booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])
booksList.Insert(2, "Roots")

printfn("after inserting at index 2")
printfn"Total %d books" booksList.Count

booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])
booksList.RemoveAt(3)

printfn("after removing from index 3")
printfn"Total %d books" booksList.Count

booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Total 6 books
0: Gone with the Wind
1: Atlas Shrugged
2: Fountainhead
3: Thornbirds
4: Rebecca
5: Narnia
after inserting at index 2
Total 7 books
0: Gone with the Wind
1: Atlas Shrugged
2: Roots
3: Fountainhead
4: Thornbirds
5: Rebecca
6: Narnia
after removing from index 3
Total 6 books
0: Gone with the Wind
1: Atlas Shrugged
2: Roots
3: Thornbirds
4: Rebecca
5: Narnia

Plik Dictionary<'TKey, 'TValue> class jest zmiennym odpowiednikiem struktury danych mapy F # i zawiera wiele takich samych funkcji.

Podsumowując z rozdziału Mapa w języku F #, mapa to specjalny rodzaj zestawu, który kojarzy wartości z kluczem.

Tworzenie słownika mutowalnego

Zmienne słowniki są tworzone przy użyciu rozszerzenia newsłowo kluczowe i wywołanie konstruktora listy. Poniższy przykład demonstruje to -

open System.Collections.Generic
let dict = new Dictionary<string, string>()
dict.Add("1501", "Zara Ali")
dict.Add("1502","Rishita Gupta")
dict.Add("1503","Robin Sahoo")
dict.Add("1504","Gillian Megan")
printfn "Dictionary - students: %A" dict

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Dictionary - students: seq
[[1501, Zara Ali]; [1502, Rishita Gupta]; [1503, Robin Sahoo];
[1504, Gillian Megan]]

Klasa słownika (TKey, TValue)

Klasa Dictionary (TKey, TValue) reprezentuje zbiór kluczy i wartości.

Poniższe tabele zawierają właściwości, konstruktory i metody klasy List (T) -

Nieruchomości

własność Opis
Porównaj Pobiera IEqualityComparer (T), który jest używany do określania równości kluczy dla słownika.
Liczyć Pobiera liczbę par klucz / wartość zawartych w Dictionary (TKey, TValue).
Pozycja Pobiera lub ustawia wartość skojarzoną z określonym kluczem.
Klucze Pobiera kolekcję zawierającą klucze w Dictionary (TKey, TValue).
Wartości Pobiera kolekcję zawierającą wartości z Dictionary (TKey, TValue).

Konstruktorzy

Konstruktorzy Opis
Słownik (TKey, TValue) () Inicjuje nowe wystąpienie Dictionary(TKey, TValue) klasa, która jest pusta, ma domyślną pojemność początkową i używa domyślnej funkcji porównującej równość dla typu klucza.
Słownik (TKey, TValue) (IDictionary (TKey, TValue)) Inicjuje nowe wystąpienie Dictionary(TKey, TValue) klasa, która zawiera elementy skopiowane z określonego IDictionary(TKey, TValue) i używa domyślnej funkcji porównującej równość dla typu klucza.
Słownik (TKey, TValue) (IEqualityComparer (TKey)) Inicjuje nowe wystąpienie Dictionary(TKey, TValue) klasa, która jest pusta, ma domyślną pojemność początkową i używa określonej IEqualityComparer(T).
Słownik (TKey, TValue) (Int32) Inicjuje nowe wystąpienie Dictionary(TKey, TValue) klasa, która jest pusta, ma określoną pojemność początkową i używa domyślnej funkcji porównującej równość dla typu klucza.
Słownik (TKey, TValue) (IDictionary (TKey, TValue), IEqualityComparer (TKey)) Inicjuje nowe wystąpienie Dictionary(TKey, TValue) klasa, która zawiera elementy skopiowane z określonego IDictionary(TKey, TValue) i używa określonego IEqualityComparer(T).
Słownik (TKey, TValue) (Int32, IEqualityComparer (TKey)) Inicjuje nowe wystąpienie Dictionary(TKey, TValue) klasa, która jest pusta, ma określoną pojemność początkową i używa określonej IEqualityComparer(T).
Słownik (TKey, TValue) (SerializationInfo, StreamingContext) Inicjuje nowe wystąpienie ictionary(TKey, TValue) klasa z zserializowanymi danymi.

Metody

metoda Opis
Dodaj Dodaje określony klucz i wartość do słownika.
Jasny Usuwa wszystkie klucze i wartości ze słownika (TKey, TValue).
ContainsKey Określa, czy Dictionary (TKey, TValue) zawiera określony klucz.
ContainsValue Określa, czy Dictionary (TKey, TValue) zawiera określoną wartość.
Równa się (obiekt) Określa, czy określony obiekt jest równy bieżącemu obiektowi. (Odziedziczone po Object.)
Sfinalizować Umożliwia obiektowi próbę zwolnienia zasobów i wykonanie innych operacji czyszczenia, zanim zostanie odzyskany przez wyrzucanie elementów bezużytecznych. (Odziedziczone po Object.)
GetEnumerator Zwraca moduł wyliczający, który wykonuje iterację w Dictionary (TKey, TValue).
GetHashCode Służy jako domyślna funkcja skrótu. (Odziedziczone po Object.)
GetObjectData Implementuje interfejs System.Runtime.Serialization.ISerializable i zwraca dane potrzebne do serializacji wystąpienia Dictionary (TKey, TValue).
GetType Pobiera typ bieżącego wystąpienia. (Odziedziczone po Object.)
MemberwiseClone Tworzy płytką kopię bieżącego obiektu. (Odziedziczone po Object.)
OnDeserialization Implementuje interfejs System.Runtime.Serialization.ISerializable i wywołuje zdarzenie deserializacji po zakończeniu deserializacji.
Usunąć Usuwa wartość z określonym kluczem z Dictionary (TKey, TValue).
ToString Zwraca ciąg, który reprezentuje bieżący obiekt. (Odziedziczone po Object.)
TryGetValue Pobiera wartość skojarzoną z określonym kluczem.

Przykład

open System.Collections.Generic
let dict = new Dictionary<string, string>()

dict.Add("1501", "Zara Ali")
dict.Add("1502","Rishita Gupta")
dict.Add("1503","Robin Sahoo")
dict.Add("1504","Gillian Megan")

printfn "Dictionary - students: %A" dict
printfn "Total Number of Students: %d" dict.Count
printfn "The keys: %A" dict.Keys
printf"The Values: %A" dict.Values

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Dictionary - students: seq
[[1501, Zara Ali]; [1502, Rishita Gupta]; [1503, Robin Sahoo];
[1504, Gillian Megan]]
Total Number of Students: 4
The keys: seq ["1501"; "1502"; "1503"; "1504"]
The Values: seq ["Zara Ali"; "Rishita Gupta"; "Robin Sahoo"; "Gillian Megan"]

Podstawowe wejście Wyjście obejmuje -

  • Czytanie i pisanie w konsoli.
  • Czytanie zi zapisywanie do pliku.

Moduł Core.Printf

Użyliśmy printf i printfn do zapisu w konsoli. W tej sekcji przyjrzymy się szczegółomPrintf moduł F #.

Oprócz powyższych funkcji, moduł Core.Printf F # ma różne inne metody drukowania i formatowania przy użyciu znaczników% jako symboli zastępczych. Poniższa tabela przedstawia metody wraz z krótkim opisem -

Wartość Opis
bprintf: StringBuilder → BuilderFormat <'T> →' T Drukuje do StringBuilder.
eprintf: TextWriterFormat <'T> →' T Wyświetla sformatowane wyjście na stderr.
eprintfn: TextWriterFormat <'T> →' T Wyświetla sformatowane dane wyjściowe na stderr, dodając nowy wiersz.
failwithf: StringFormat <'T,' Wynik> → 'T Drukuje do bufora łańcuchowego i zgłasza wyjątek z podanym wynikiem.
fprintf: TextWriter → TextWriterFormat <'T> →' T Drukuje dla autora tekstu.
fprintfn: TextWriter → TextWriterFormat <'T> →' T Drukuje do pisarza tekstu, dodając nowy wiersz.
kbprintf: (jednostka → 'Wynik) → StringBuilder → BuilderFormat <' T, 'Wynik> →' T Podobnie jak bprintf, ale wywołuje określoną funkcję w celu wygenerowania wyniku.
kfprintf: (jednostka → 'Wynik) → TextWriter → TextWriterFormat <' T, 'Wynik> →' T Podobnie jak fprintf, ale wywołuje określoną funkcję w celu wygenerowania wyniku.
kprintf: (string → 'Wynik) → StringFormat <' T, 'Wynik> →' T Podobnie jak printf, ale wywołuje określoną funkcję w celu wygenerowania wyniku. Na przykład, pozwalają one drukować na wymuszenie wyrównania po wprowadzeniu całego wyjścia na kanał, ale nie wcześniej.
ksprintf: (string → 'Wynik) → StringFormat <' T, 'Wynik> →' T Podobnie jak sprintf, ale wywołuje określoną funkcję, aby wygenerować wynik.
printf: TextWriterFormat <'T> →' T Wyświetla sformatowane dane wyjściowe na standardowe wyjście.
printfn: TextWriterFormat <'T> →' T Wyświetla sformatowane dane wyjściowe na standardowe wyjście, dodając nowy wiersz.
sprintf: StringFormat <'T> →' T Drukuje do ciągu przy użyciu wewnętrznego bufora ciągu i zwraca wynik jako ciąg.

Specyfikacje formatu

Specyfikacje formatu są używane do formatowania wejścia lub wyjścia, zgodnie z potrzebami programistów.

Są to ciągi znaków ze znacznikami% wskazującymi symbole zastępcze formatu.

Składnia symboli zastępczych formatu to -

%[flags][width][.precision][type]

Plik type jest interpretowane jako -

Rodzaj Opis
%b Formaty a bool, sformatowany jako true lub false.
%do Formatuje znak.
% s Formaty a string, sformatowany jako zawartość, bez interpretowania żadnych znaków zmiany znaczenia.
% d,% i Formatuje dowolny podstawowy typ liczby całkowitej sformatowany jako dziesiętna liczba całkowita ze znakiem, jeśli podstawowy typ liczby całkowitej jest podpisany.
% u Formatuje dowolny podstawowy typ liczby całkowitej sformatowany jako dziesiętna liczba całkowita bez znaku.
% x Formatuje dowolny podstawowy typ liczby całkowitej sformatowany jako liczba szesnastkowa bez znaku, używając małych liter od a do f.
% X Formatuje dowolny podstawowy typ liczby całkowitej sformatowany jako liczba szesnastkowa bez znaku, używając wielkich liter od A do F.
% o Formatuje dowolny podstawowy typ liczby całkowitej sformatowany jako ósemkowa liczba całkowita bez znaku.
% e,% E,% f,% F,% g,% G Formatuje dowolny podstawowy typ zmiennoprzecinkowy (float, float32) sformatowany przy użyciu specyfikacji formatu zmiennoprzecinkowego w stylu C.
% e,% E Formatuje wartość ze znakiem mającą postać [-] d.dddde [znak] ddd, gdzie d to pojedyncza cyfra dziesiętna, dddd to jedna lub więcej cyfr dziesiętnych, ddd to dokładnie trzy cyfry dziesiętne, a znak to + lub -.
%fa Formatuje wartość ze znakiem w postaci [-] dddd.dddd, gdzie dddd to jedna lub więcej cyfr dziesiętnych. Liczba cyfr przed przecinkiem zależy od wielkości liczby, a liczba cyfr po przecinku zależy od wymaganej dokładności.
% g,% G Formatuje podpisaną wartość drukowaną w formacie f lub e, w zależności od tego, który jest bardziej zwarty dla podanej wartości i dokładności.
% M Formatuje wartość Decimal.
% O Formatuje dowolną wartość, drukowaną przez umieszczenie obiektu w ramce i użycie jego ToString metoda.
% A,% + A Formatuje dowolną wartość, drukowaną z domyślnymi ustawieniami układu. Użyj% + A, aby wydrukować strukturę związków rozłącznych z reprezentacjami wewnętrznymi i prywatnymi.
%za

Ogólny specyfikator formatu wymaga dwóch argumentów. Pierwszy argument to funkcja, która przyjmuje dwa argumenty: pierwszy parametr kontekstu odpowiedniego typu dla danej funkcji formatującej (na przykład TextWriter), a drugi wartość do wydrukowania i która zwraca lub zwraca odpowiedni tekst.

Drugi argument to określona wartość do wydrukowania.

% t Ogólny specyfikator formatu wymaga jednego argumentu: funkcji, która przyjmuje parametr kontekstu odpowiedniego typu dla danej funkcji formatującej (aTextWriter) i która albo wyprowadza, albo zwraca odpowiedni tekst. Podstawowe typy liczb całkowitych tobyte, sbyte, int16, uint16, int32, uint32, int64, uint64, nativeint, i unativeint. Podstawowe typy zmiennoprzecinkowe to float i float32.

Plik widthjest opcjonalnym parametrem. Jest to liczba całkowita określająca minimalną szerokość wyniku. Na przykład% 5d drukuje liczbę całkowitą z co najmniej 5 znakami spacji.

Ważny flags opisano w poniższej tabeli -

Wartość Opis
0 Określa, aby dodać zera zamiast spacji, aby uzyskać wymaganą szerokość.
- Określa wyrównywanie wyniku do lewej w określonej szerokości.
+ Określa, że ​​należy dodać znak +, jeśli liczba jest dodatnia (aby dopasować znak - dla liczb ujemnych).
' ' (przestrzeń) Określa, że ​​należy dodać dodatkową spację, jeśli liczba jest dodatnia (aby dopasować znak - dla liczb ujemnych).
# Nieważny.

Przykład

printf "Hello "
printf "World"
printfn ""
printfn "Hello "
printfn "World"
printf "Hi, I'm %s and I'm a %s" "Rohit" "Medical Student"

printfn "d: %f" 212.098f
printfn "e: %f" 504.768f

printfn "x: %g" 212.098f
printfn "y: %g" 504.768f

printfn "x: %e" 212.098f
printfn "y: %e" 504.768f
printfn "True: %b" true

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Hello World
Hello
World
Hi, I'm Rohit and I'm a Medical Studentd: 212.098000
e: 504.768000
x: 212.098
y: 504.768
x: 2.120980e+002
y: 5.047680e+002
True: true

Klasa konsoli

Ta klasa jest częścią platformy .NET. Reprezentuje standardowe strumienie wejścia, wyjścia i błędów dla aplikacji konsolowych.

Zapewnia różne metody odczytu i zapisu w konsoli. Poniższa tabela przedstawia metody -

metoda Opis
Brzęczyk() Odtwarza dźwięk sygnału dźwiękowego przez głośnik konsoli.
Sygnał dźwiękowy (Int32, Int32) Odtwarza dźwięk sygnału dźwiękowego o określonej częstotliwości i czasie trwania przez głośnik konsoli.
Jasny Czyści bufor konsoli i odpowiednie okno konsoli z wyświetlanymi informacjami.
MoveBufferArea (Int32, Int32, Int32, Int32, Int32, Int32) Kopiuje określony obszar źródłowy buforu ekranu do określonego obszaru docelowego.
MoveBufferArea (Int32, Int32, Int32, Int32, Int32, Int32, Char, ConsoleColor, ConsoleColor) Kopiuje określony obszar źródłowy buforu ekranu do określonego obszaru docelowego.
OpenStandardError () Uzyskuje standardowy strumień błędów.
OpenStandardError (Int32) Uzyskuje standardowy strumień błędów, który jest ustawiony na określony rozmiar buforu.
OpenStandardInput () Uzyskuje standardowy strumień wejściowy.
OpenStandardInput (Int32) Uzyskuje standardowy strumień wejściowy, który jest ustawiony na określony rozmiar buforu.
OpenStandardOutput () Uzyskuje standardowy strumień wyjściowy.
OpenStandardOutput (Int32) Uzyskuje standardowy strumień wyjściowy, który jest ustawiony na określony rozmiar buforu.
Czytać Odczytuje następny znak ze standardowego strumienia wejściowego.
Kluczem przeczytać() Uzyskuje następny znak lub klawisz funkcyjny naciśnięty przez użytkownika. Wciśnięty klawisz jest wyświetlany w oknie konsoli.
ReadKey (wartość logiczna) Uzyskuje następny znak lub klawisz funkcyjny naciśnięty przez użytkownika. Wciśnięty klawisz jest opcjonalnie wyświetlany w oknie konsoli.
Czytaj linię Odczytuje następny wiersz znaków ze standardowego strumienia wejściowego.
ResetColor Ustawia domyślne kolory konsoli pierwszego planu i tła.
SetBufferSize Ustawia wysokość i szerokość obszaru bufora ekranu na określone wartości.
SetCursorPosition Ustawia pozycję kursora.
SetError Ustawia właściwość Error na określony obiekt TextWriter .
SetIn Ustawia właściwość In na określony obiekt TextReader .
SetOut Ustawia właściwość Out na określony obiekt TextWriter .
SetWindowPosition Ustawia pozycję okna konsoli względem bufora ekranu.
SetWindowSize Ustawia wysokość i szerokość okna konsoli na określone wartości.
Napisz (wartość logiczna) Zapisuje tekstową reprezentację określonej wartości logicznej w standardowym strumieniu wyjściowym.
Napisz (znak) Zapisuje określoną wartość znaku Unicode w standardowym strumieniu wyjściowym.
Napisz (Char []) Zapisuje określoną tablicę znaków Unicode w standardowym strumieniu wyjściowym.
Zapis (dziesiętny) Zapisuje tekstową reprezentację określonej wartości Decimal w standardowym strumieniu wyjściowym.
Napisz (podwójne) Zapisuje tekstową reprezentację określonej wartości zmiennoprzecinkowej o podwójnej precyzji w standardowym strumieniu wyjściowym.
Napisz (Int32) Zapisuje tekstową reprezentację określonej 32-bitowej liczby całkowitej ze znakiem w standardowym strumieniu wyjściowym.
Napisz (Int64) Zapisuje tekstową reprezentację określonej 64-bitowej liczby całkowitej ze znakiem w standardowym strumieniu wyjściowym.
Napisz (obiekt) Zapisuje tekstową reprezentację określonego obiektu w standardowym strumieniu wyjściowym.
Napisz (pojedynczy) Zapisuje tekstową reprezentację określonej wartości zmiennoprzecinkowej o pojedynczej precyzji w standardowym strumieniu wyjściowym.
Napisz (ciąg) Zapisuje określoną wartość ciągu w standardowym strumieniu wyjściowym.
Napisz (UInt32) Zapisuje tekstową reprezentację określonej 32-bitowej liczby całkowitej bez znaku w standardowym strumieniu wyjściowym.
Napisz (UInt64) Zapisuje tekstową reprezentację określonej 64-bitowej liczby całkowitej bez znaku w standardowym strumieniu wyjściowym.
Napisz (ciąg, obiekt) Zapisuje tekstową reprezentację określonego obiektu w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
Napisz (ciąg, obiekt []) Zapisuje tekstową reprezentację określonej tablicy obiektów w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
Napisz (Char [], Int32, Int32) Zapisuje określoną podtablicę znaków Unicode w standardowym strumieniu wyjściowym.
Napisz (ciąg, obiekt, obiekt) Zapisuje tekstową reprezentację określonych obiektów w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
Napisz (ciąg, obiekt, obiekt, obiekt) Zapisuje tekstową reprezentację określonych obiektów w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
Napisz (ciąg, obiekt, obiekt, obiekt, obiekt) Zapisuje tekstową reprezentację określonych obiektów i listę parametrów o zmiennej długości w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
WriteLine () Zapisuje bieżący terminator linii w standardowym strumieniu wyjściowym.
WriteLine (Boolean) Zapisuje tekstową reprezentację określonej wartości logicznej, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (znak) Zapisuje określony znak Unicode, po którym następuje bieżący terminator wiersza, wartość w standardowym strumieniu wyjściowym.
WriteLine (Char []) Zapisuje określoną tablicę znaków Unicode, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (dziesiętny) Zapisuje tekstową reprezentację określonej wartości Decimal, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (podwójne) Zapisuje tekstową reprezentację określonej wartości zmiennoprzecinkowej o podwójnej precyzji, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (Int32) Zapisuje tekstową reprezentację określonej 32-bitowej liczby całkowitej ze znakiem, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (Int64) Zapisuje tekstową reprezentację określonej 64-bitowej wartości całkowitej ze znakiem, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (obiekt) Zapisuje tekstową reprezentację określonego obiektu, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (pojedynczy) Zapisuje tekstową reprezentację określonej wartości zmiennoprzecinkowej o pojedynczej precyzji, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (ciąg) Zapisuje określoną wartość ciągu, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (UInt32) Zapisuje tekstową reprezentację określonej 32-bitowej liczby całkowitej bez znaku, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (UInt64) Zapisuje tekstową reprezentację określonej 64-bitowej wartości całkowitej bez znaku, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (ciąg, obiekt) Zapisuje tekstową reprezentację określonego obiektu, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
WriteLine (ciąg, obiekt []) Zapisuje tekstową reprezentację określonej tablicy obiektów, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
WriteLine (Char [], Int32, Int32) Zapisuje określoną podtablicę znaków Unicode, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym.
WriteLine (ciąg, obiekt, obiekt) Zapisuje tekstową reprezentację określonych obiektów, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
WriteLine (ciąg, obiekt, obiekt, obiekt) Zapisuje tekstową reprezentację określonych obiektów, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.
WriteLine (ciąg, obiekt, obiekt, obiekt, obiekt) Zapisuje tekstową reprezentację określonych obiektów i listę parametrów o zmiennej długości, po której następuje bieżący terminator wiersza, w standardowym strumieniu wyjściowym przy użyciu określonych informacji o formacie.

Poniższy przykład demonstruje czytanie z konsoli i pisanie do niej -

Przykład

open System
let main() =
   Console.Write("What's your name? ")
   let name = Console.ReadLine()
   Console.Write("Hello, {0}\n", name)
   Console.WriteLine(System.String.Format("Big Greetings from {0} and {1}", "TutorialsPoint", "Absoulte Classes"))
   Console.WriteLine(System.String.Format("|{0:yyyy-MMM-dd}|", System.DateTime.Now))
main()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

What's your name? Kabir
Hello, Kabir
Big Greetings from TutorialsPoint and Absoulte Classes
|2015-Jan-05|

Przestrzeń nazw System.IO

Przestrzeń nazw System.IO zawiera wiele przydatnych klas do wykonywania podstawowych operacji we / wy.

Zawiera typy lub klasy, które umożliwiają odczytywanie i zapisywanie do plików i strumieni danych oraz typy, które zapewniają podstawową obsługę plików i katalogów.

Klasy przydatne przy pracy z systemem plików -

  • Klasa System.IO.File służy do tworzenia, dołączania i usuwania plików.
  • Klasa System.IO.Directory służy do tworzenia, przenoszenia i usuwania katalogów.
  • Klasa System.IO.Path wykonuje operacje na łańcuchach, które reprezentują ścieżki plików.
  • Klasa System.IO.FileSystemWatcher umożliwia użytkownikom nasłuchiwanie katalogu pod kątem zmian.

Klasy przydatne do pracy ze strumieniami (sekwencja bajtów) -

  • Klasa System.IO.StreamReader służy do odczytywania znaków ze strumienia.
  • Klasa System.IO.StreamWriter służy do zapisywania znaków w strumieniu.
  • Klasa System.IO.MemoryStream tworzy w pamięci strumień bajtów.

Poniższa tabela przedstawia wszystkie klasy znajdujące się w przestrzeni nazw wraz z krótkim opisem -

Klasa Opis
BinaryReader Odczytuje pierwotne typy danych jako wartości binarne w określonym kodowaniu.
BinaryWriter Zapisuje typy pierwotne w postaci binarnej w strumieniu i obsługuje pisanie ciągów w określonym kodowaniu.
BufferedStream Dodaje warstwę buforującą do operacji odczytu i zapisu w innym strumieniu.
Informator Udostępnia statyczne metody tworzenia, przenoszenia i wyliczania w katalogach i podkatalogach.
DirectoryInfo Udostępnia metody instancji służące do tworzenia, przenoszenia i wyliczania w katalogach i podkatalogach.
DirectoryNotFoundException Wyjątek, który jest generowany, gdy nie można znaleźć części pliku lub katalogu.
DriveInfo Zapewnia dostęp do informacji na dysku.
DriveNotFoundException Wyjątek, który jest generowany podczas próby uzyskania dostępu do dysku lub udziału, który nie jest dostępny.
EndOfStreamException Wyjątek, który jest generowany podczas próby odczytu po zakończeniu strumienia.
ErrorEventArgs Zawiera dane dla zdarzenia FileSystemWatcher.Error.
Plik Zapewnia statyczne metody tworzenia, kopiowania, usuwania, przenoszenia i otwierania pojedynczego pliku oraz pomaga w tworzeniu obiektów FileStream.
FileFormatException Wyjątek, który jest generowany, gdy plik wejściowy lub strumień danych, który powinien być zgodny z określoną specyfikacją formatu pliku, jest nieprawidłowo sformułowany.
Informacja o pliku Zapewnia właściwości i metody instancji do tworzenia, kopiowania, usuwania, przenoszenia i otwierania plików oraz pomaga w tworzeniu obiektów FileStream.
FileLoadException Wyjątek, który jest generowany, gdy zostanie znaleziony zestaw zarządzany, ale nie można go załadować.
FileNotFoundException Wyjątek, który jest generowany, gdy próba uzyskania dostępu do pliku, który nie istnieje na dysku, kończy się niepowodzeniem.
FileStream Udostępnia Stream wokół pliku, obsługując synchroniczne i asynchroniczne operacje odczytu i zapisu.
FileSystemEventArgs Zawiera dane dotyczące zdarzeń w katalogu - Zmienione, Utworzone, Usunięte.
FileSystemInfo Udostępnia klasę bazową dla obiektów FileInfo i DirectoryInfo.
FileSystemWatcher Nasłuchuje powiadomień o zmianach systemu plików i zgłasza zdarzenia, gdy katalog lub plik w katalogu ulegają zmianie.
InternalBufferOverflowException Wyjątek zgłaszany w przypadku przepełnienia buforu wewnętrznego.
InvalidDataException Wyjątek, który jest generowany, gdy strumień danych ma nieprawidłowy format.
IODescriptionAttribute Ustawia opis, który projektanci wizualni mogą wyświetlać podczas odwoływania się do zdarzenia, rozszerzenia lub właściwości.
IOException Wyjątek, który jest generowany, gdy wystąpi błąd we / wy.
MemoryStream Tworzy strumień, którego magazynem zapasowym jest pamięć.
Ścieżka Wykonuje operacje na instancjach typu String, które zawierają informacje o ścieżce do pliku lub katalogu. Operacje te są wykonywane w sposób wieloplatformowy.
PathTooLongException Wyjątek, który jest generowany, gdy ścieżka lub nazwa pliku jest dłuższa niż maksymalna długość zdefiniowana przez system.
PipeException Zgłaszane, gdy wystąpi błąd w nazwanym potoku.
RenamedEventArgs Zawiera dane dla zdarzenia o zmienionej nazwie.
Strumień Zawiera ogólny widok sekwencji bajtów. To jest klasa abstrakcyjna.
StreamReader Implementuje TextReader, który odczytuje znaki ze strumienia bajtów w określonym kodowaniu.
StreamWriter Implementuje TextWriter do pisania znaków w strumieniu w określonym kodowaniu. Aby przeglądać kod źródłowy .NET Framework dla tego typu, zobacz źródło odniesienia.
StringReader Implementuje TextReader, który odczytuje ciąg.
StringWriter Implementuje TextWriter do zapisywania informacji w ciągu. Informacje są przechowywane w źródłowym StringBuilder.
TextReader Reprezentuje czytelnika, który może odczytać sekwencyjną serię znaków.
TextWriter Reprezentuje pisarza, który może pisać sekwencyjną serię znaków. Ta klasa jest abstrakcyjna.
UnmanagedMemoryAccessor Zapewnia losowy dostęp do niezarządzanych bloków pamięci z kodu zarządzanego.
UnmanagedMemoryStream Zapewnia dostęp do niezarządzanych bloków pamięci z kodu zarządzanego.
WindowsRuntimeStorageExtensions Zawiera metody rozszerzające dla interfejsów IStorageFile i IStorageFolder w środowisku wykonawczym systemu Windows podczas tworzenia aplikacji ze Sklepu Windows.
WindowsRuntimeStreamExtensions Zawiera metody rozszerzenia służące do konwertowania między strumieniami w środowisku wykonawczym systemu Windows i strumieniami zarządzanymi w aplikacji .NET for Windows Store.

Przykład

Poniższy przykład tworzy plik o nazwie test.txt, zapisuje tam wiadomość, odczytuje tekst z pliku i drukuje go na konsoli.

Note - Ilość kodu potrzebnego do tego jest zaskakująco mniejsza!

open System.IO // Name spaces can be opened just as modules
File.WriteAllText("test.txt", "Hello There\n Welcome to:\n Tutorials Point")
let msg = File.ReadAllText("test.txt")
printfn "%s" msg

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Hello There
Welcome to:
Tutorials Point

Generics pozwalają opóźnić specyfikację typu danych elementów programistycznych w klasie lub metodzie, dopóki nie zostaną one faktycznie użyte w programie. Innymi słowy, typy ogólne umożliwiają napisanie klasy lub metody, która może działać z dowolnym typem danych.

Piszesz specyfikacje dla klasy lub metody, zastępując parametry dla typów danych. Gdy kompilator napotka konstruktor dla klasy lub wywołanie funkcji dla metody, generuje kod obsługujący określony typ danych.

W języku F # wartości funkcji, metody, właściwości i typy agregatów, takie jak klasy, rekordy i związki rozłączne, mogą być ogólne.

Konstrukcje ogólne zawierają co najmniej jeden parametr typu. Funkcje i typy ogólne umożliwiają pisanie kodu, który działa z różnymi typami bez powtarzania kodu dla każdego typu.

Składnia

Składnia do pisania konstrukcji ogólnej jest następująca -

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
   function-body

// Explicitly generic method.
[ static ] member object-identifer.method-name<type-parameters> parameter-list [ return-type ] =
   method-body

// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition

Przykłady

(* Generic Function *)
let printFunc<'T> x y =
   printfn "%A, %A" x y

printFunc<float> 10.0 20.0

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

10.0, 20.0

Możesz również utworzyć funkcję ogólną, używając składni pojedynczego cudzysłowu -

(* Generic Function *)
let printFunction (x: 'a) (y: 'a) =
   printfn "%A %A" x y

printFunction 10.0 20.0

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

10.0 20.0

Należy pamiętać, że podczas korzystania z funkcji lub metod ogólnych może nie być konieczne określanie argumentów typu. Jednak w przypadku niejednoznaczności można podać argumenty typu w nawiasach ostrych, tak jak to zrobiliśmy w pierwszym przykładzie.

Jeśli masz więcej niż jeden typ, oddziel argumenty wielu typów przecinkami.

Klasa ogólna

Podobnie jak funkcje ogólne, można również pisać klasy ogólne. Poniższy przykład demonstruje to -

type genericClass<'a> (x: 'a) =
   do printfn "%A" x

let gr = new genericClass<string>("zara")
let gs = genericClass( seq { for i in 1 .. 10 -> (i, i*i) } )

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

"zara"
seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]

Delegat to zmienna typu referencyjnego, która przechowuje odwołanie do metody. Odniesienie można zmienić w czasie wykonywania. Delegaci F # są podobne do wskaźników do funkcji w C lub C ++.

Deklarowanie delegatów

Deklaracja delegata określa metody, do których może odwoływać się delegat. Delegat może odwoływać się do metody, która ma taki sam podpis, jak podpis delegata.

Składnia deklaracji delegata to -

type delegate-typename = delegate of type1 -> type2

Na przykład rozważmy delegatów -

// Delegate1 works with tuple arguments.
type Delegate1 = delegate of (int * int) -> int
// Delegate2 works with curried arguments.
type Delegate2 = delegate of int * int -> int

Obaj delegaci mogą służyć do odwoływania się do dowolnej metody, która ma dwa parametry int i zwraca zmienną typu int .

W składni -

  • type1 reprezentuje typ (y) argumentów.

  • type2 reprezentuje typ zwrotu.

Uwaga -

  • Typy argumentów są automatycznie przypisywane.

  • Delegatów można dołączyć do wartości funkcji i metod statycznych lub instancji.

  • Wartości funkcji F # można przekazywać bezpośrednio jako argumenty do konstruktorów delegatów.

  • W przypadku metody statycznej delegat jest wywoływany przy użyciu nazwy klasy i metody. W przypadku metody instancji używana jest nazwa instancji obiektu i metody.

  • Metoda Invoke dla typu delegata wywołuje hermetyzowaną funkcję.

  • Ponadto delegatów można przekazywać jako wartości funkcji, odwołując się do nazwy metody Invoke bez nawiasów.

Poniższy przykład ilustruje koncepcję -

Przykład

type Myclass() =
   static member add(a : int, b : int) =
      a + b
   static member sub (a : int) (b : int) =
      a - b
   member x.Add(a : int, b : int) =
      a + b
   member x.Sub(a : int) (b : int) =
      a - b

// Delegate1 works with tuple arguments.
type Delegate1 = delegate of (int * int) -> int
// Delegate2 works with curried arguments.
type Delegate2 = delegate of int * int -> int

let InvokeDelegate1 (dlg : Delegate1) (a : int) (b: int) =
   dlg.Invoke(a, b)
let InvokeDelegate2 (dlg : Delegate2) (a : int) (b: int) =
   dlg.Invoke(a, b)

// For static methods, use the class name, the dot operator, and the
// name of the static method.
let del1 : Delegate1 = new Delegate1( Myclass.add )
let del2 : Delegate2 = new Delegate2( Myclass.sub )

let mc = Myclass()
// For instance methods, use the instance value name, the dot operator, and the instance method name.

let del3 : Delegate1 = new Delegate1( mc.Add )
let del4 : Delegate2 = new Delegate2( mc.Sub )

for (a, b) in [ (400, 200); (100, 45) ] do
   printfn "%d + %d = %d" a b (InvokeDelegate1 del1 a b)
   printfn "%d - %d = %d" a b (InvokeDelegate2 del2 a b)
   printfn "%d + %d = %d" a b (InvokeDelegate1 del3 a b)
   printfn "%d - %d = %d" a b (InvokeDelegate2 del4 a b)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

400 + 200 = 600
400 - 200 = 200
400 + 200 = 600
400 - 200 = 200
100 + 45 = 145
100 - 45 = 55
100 + 45 = 145
100 - 45 = 55

Wyliczenie to zbiór nazwanych stałych całkowitych.

W F #, enumerations, znany również jako enums,są typami całkowitymi, w których etykiety są przypisane do podzbioru wartości. Możesz ich używać zamiast literałów, aby kod był bardziej czytelny i łatwiejszy w utrzymaniu.

Deklarowanie wyliczeń

Ogólna składnia deklarowania wyliczenia to -

type enum-name =
   | value1 = integer-literal1
   | value2 = integer-literal2
...

Poniższy przykład demonstruje użycie wyliczeń -

Przykład

// Declaration of an enumeration.
type Days =
   | Sun = 0
   | Mon = 1
   | Tues = 2
   | Wed = 3
   | Thurs = 4
   | Fri = 5
   | Sat = 6

// Use of an enumeration.
let weekend1 : Days = Days.Sat
let weekend2 : Days = Days.Sun
let weekDay1 : Days = Days.Mon

printfn "Monday: %A" weekDay1
printfn "Saturday: %A" weekend1
printfn "Sunday: %A" weekend2

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Monday: Mon
Saturday: Sat
Sunday: Sun

Dopasowywanie wzorców umożliwia „porównywanie danych z logiczną strukturą lub strukturami, rozkładanie danych na części składowe lub wyodrębnianie informacji z danych na różne sposoby”.

Innymi słowy, zapewnia bardziej elastyczny i skuteczniejszy sposób testowania danych pod kątem szeregu warunków i wykonywania niektórych obliczeń w oparciu o spełniony warunek.

Koncepcyjnie przypomina serię stwierdzeń „jeśli… to”.

Składnia

Mówiąc ogólnie, dopasowanie wzorców jest zgodne z tą składnią w języku F # -

match expr with
| pat1 - result1
| pat2 -> result2
| pat3 when expr2 -> result3
| _ -> defaultResult

Gdzie,

  • Każdy | symbol określa warunek.
  • Symbol -> oznacza „jeśli warunek jest prawdziwy, zwróć tę wartość ...”.
  • Symbol _ zapewnia domyślny wzorzec, co oznacza, że ​​pasuje do wszystkich innych rzeczy, takich jak symbol wieloznaczny.

Przykład 1

Poniższy przykład oblicza liczby Fibonacciego przy użyciu składni dopasowania wzorców -

let rec fib n =
   match n with
   | 0 -> 0
   | 1 -> 1
   | _ -> fib (n - 1) + fib (n - 2)
for i = 1 to 10 do
   printfn "Fibonacci %d: %d" i (fib i)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Fibonacci 1: 1
Fibonacci 2: 1
Fibonacci 3: 2
Fibonacci 4: 3
Fibonacci 5: 5
Fibonacci 6: 8
Fibonacci 7: 13
Fibonacci 8: 21
Fibonacci 9: 34
Fibonacci 10: 55

Możesz również połączyć wiele warunków, które zwracają tę samą wartość. Na przykład -

Przykład 2

let printSeason month =
   match month with
   | "December" | "January" | "February" -> printfn "Winter"
   | "March" | "April" -> printfn "Spring"
   | "May" | "June" -> printfn "Summer"
   | "July" | "August" -> printfn "Rainy"
   | "September" | "October" | "November" -> printfn "Autumn"
   | _ -> printfn "Season depends on month!"

printSeason "February"
printSeason "April"
printSeason "November"
printSeason "July"

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Winter
Spring
Autumn
Rainy

Funkcje dopasowania wzorców

F # umożliwia pisanie funkcji dopasowywania wzorców przy użyciu function słowo kluczowe -

let getRate = function
   | "potato" -> 10.00
   | "brinjal" -> 20.50
   | "cauliflower" -> 21.00
   | "cabbage" -> 8.75
   | "carrot" -> 15.00
   | _ -> nan (* nan is a special value meaning "not a number" *)

printfn "%g"(getRate "potato")
printfn "%g"(getRate "brinjal")
printfn "%g"(getRate "cauliflower")
printfn "%g"(getRate "cabbage")
printfn "%g"(getRate "carrot")

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

10
20.5
21
8.75
15

Dodawanie filtrów lub osłon do wzorców

Możesz dodać filtry lub osłony do wzorców przy użyciu rozszerzenia when słowo kluczowe.

Przykład 1

let sign = function
   | 0 -> 0
   | x when x < 0 -> -1
   | x when x > 0 -> 1

printfn "%d" (sign -20)
printfn "%d" (sign 20)
printfn "%d" (sign 0)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

-1
1
0

Przykład 2

let compareInt x =
   match x with
   | (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2
   | (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
   | (var1, var2) -> printfn "%d equals %d" var1 var2

compareInt (11,25)
compareInt (72, 10)
compareInt (0, 0)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

11 is less than 25
72 is greater than 10
0 equals 0

Dopasowywanie wzorców z krotkami

Poniższy przykład ilustruje dopasowanie wzorca za pomocą krotek -

let greeting (name, subject) =
   match (name, subject) with
   | ("Zara", _) -> "Hello, Zara"
   | (name, "English") -> "Hello, " + name + " from the department of English"
   | (name, _) when subject.StartsWith("Comp") -> "Hello, " + name + " from the department of Computer Sc."
   | (_, "Accounts and Finance") -> "Welcome to the department of Accounts and Finance!"
   | _ -> "You are not registered into the system"

printfn "%s" (greeting ("Zara", "English"))
printfn "%s" (greeting ("Raman", "Computer Science"))
printfn "%s" (greeting ("Ravi", "Mathematics"))

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Hello, Zara
Hello, Raman from the department of Computer Sc.
You are not registered into the system

Dopasowanie wzorców do rekordów

Poniższy przykład demonstruje dopasowanie wzorców z rekordami -

type Point = { x: float; y: float }
let evaluatePoint (point: Point) =
   match point with
   | { x = 0.0; y = 0.0 } -> printfn "Point is at the origin."
   | { x = xVal; y = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
   | { x = 0.0; y = yVal } -> printfn "Point is on the y-axis. Value is %f." yVal
   | { x = xVal; y = yVal } -> printfn "Point is at (%f, %f)." xVal yVal

evaluatePoint { x = 0.0; y = 0.0 }
evaluatePoint { x = 10.0; y = 0.0 }
evaluatePoint { x = 0.0; y = 10.0 }
evaluatePoint { x = 10.0; y = 10.0 }

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Point is at the origin.
Point is on the x-axis. Value is 10.000000.
Point is on the y-axis. Value is 10.000000.
Point is at (10.000000, 10.000000).

Wyjątkiem jest problem, który pojawia się podczas wykonywania programu. Wyjątek F # jest odpowiedzią na wyjątkowe okoliczności, które pojawiają się podczas działania programu, na przykład próbę podzielenia przez zero.

Wyjątki umożliwiają przekazanie kontroli z jednej części programu do drugiej. Obsługa wyjątków F # zapewnia następujące konstrukcje -

Zbudować Opis
podnieść wyr Podnosi podany wyjątek.
failwith expr Podnosi System.Exception wyjątek.
spróbuj wyrażenie z regułami Łapie wyrażenia pasujące do reguł wzorców.
spróbuj wyrażenie w końcu wyr Wykonanie finally wyrażenie zarówno w przypadku pomyślnego obliczenia, jak i zgłoszenia wyjątku.
| :? ArgumentException Reguła pasująca do danego typu wyjątku .NET.
| :? ArgumentException jako e Reguła pasująca do danego typu wyjątku .NET, wiążąca nazwę e do wartości obiektu wyjątku.
| Błąd (msg) → wyr Reguła pasująca do danego wyjątku F # przenoszącego dane.
| exn → wyr Reguła pasująca do każdego wyjątku, wiążąca nazwę exn do wartości obiektu wyjątku.
| exn when expr → expr Reguła pasująca do wyjątku pod zadanym warunkiem, wiążąca nazwę exn do wartości obiektu wyjątku.

Zacznijmy od podstawowej składni obsługi wyjątków.

Składnia

Podstawowa składnia dla bloku obsługi wyjątków F # to -

exception exception-type of argument-type

Gdzie,

  • exception-type to nazwa nowego typu wyjątku F #.

  • argument-type reprezentuje typ argumentu, który można podać, gdy zgłosisz wyjątek tego typu.

  • Można określić wiele argumentów, używając typu krotki dla typu argumentu.

Plik try...with wyrażenie jest używane do obsługi wyjątków w języku F #.

Składnia try… z wyrażeniem to -

try
   expression1
with
   | pattern1 -> expression2
   | pattern2 -> expression3
...

Plik try...finally wyrażenie umożliwia wykonanie czystego kodu, nawet jeśli blok kodu zgłasza wyjątek.

Składnia try… na końcu wyrażenie to -

try
   expression1
finally
   expression2

Plik raiseFunkcja służy do wskazania, że ​​wystąpił błąd lub wyjątkowy stan. Przechwytuje również informacje o błędzie w obiekcie wyjątku.

Składnia funkcji podbicia to -

raise (expression)

Plik failwith funkcja generuje wyjątek F #.

Składnia funkcji failwith to -

failwith error-message-string

Plik invalidArg funkcja generuje wyjątek argumentu.

invalidArg parameter-name error-message-string

Przykład obsługi wyjątków

Przykład 1

Poniższy program przedstawia podstawową obsługę wyjątków za pomocą prostego try… with block -

let divisionprog x y =
   try
      Some (x / y)
   with
      | :? System.DivideByZeroException -> printfn "Division by zero!"; None

let result1 = divisionprog 100 0

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Division by zero!

Przykład 2

F # zapewnia exceptionwpisz do deklarowania wyjątków. Możesz użyć typu wyjątku bezpośrednio w filtrach w plikutry...with wyrażenie.

Poniższy przykład demonstruje to -

exception Error1 of string
// Using a tuple type as the argument type.
exception Error2 of string * int

let myfunction x y =
   try
      if x = y then raise (Error1("Equal Number Error"))
      else raise (Error2("Error Not detected", 100))
   with
      | Error1(str) -> printfn "Error1 %s" str
      | Error2(str, i) -> printfn "Error2 %s %d" str i
myfunction 20 10
myfunction 5 5

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Error2 Error Not detected 100
Error1 Equal Number Error

Przykład 3

Poniższy przykład demonstruje obsługę zagnieżdżonych wyjątków -

exception InnerError of string
exception OuterError of string

let func1 x y =
   try
      try
         if x = y then raise (InnerError("inner error"))
         else raise (OuterError("outer error"))
      with
         | InnerError(str) -> printfn "Error:%s" str
   finally
      printfn "From the finally block."

let func2 x y =
   try
      func1 x y
   with
      | OuterError(str) -> printfn "Error: %s" str

func2 100 150
func2 100 100
func2 100 120

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

From the finally block.
Error: outer error
Error:inner error
From the finally block.
From the finally block.
Error: outer error

Przykład 4

Poniższa funkcja demonstruje failwith funkcja -

let divisionFunc x y =
   if (y = 0) then failwith "Divisor cannot be zero."
   else
      x / y

let trydivisionFunc x y =
   try
      divisionFunc x y
   with
      | Failure(msg) -> printfn "%s" msg; 0

let result1 = trydivisionFunc 100 0
let result2 = trydivisionFunc 100 4
printfn "%A" result1
printfn "%A" result2

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Divisor cannot be zero.
0
25

Przykład 5

Plik invalidArgfunkcja generuje wyjątek argumentu. Poniższy program demonstruje to -

let days = [| "Sunday"; "Monday"; "Tuesday"; "Wednesday"; "Thursday"; "Friday"; "Saturday" |]
let findDay day =
   if (day > 7 || day < 1)
      then invalidArg "day" (sprintf "You have entered %d." day)
   days.[day - 1]

printfn "%s" (findDay 1)
printfn "%s" (findDay 5)
printfn "%s" (findDay 9)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Sunday
Thursday
Unhandled Exception:
System.ArgumentException: You have entered 9.
…

W zależności od systemu zostaną również wyświetlone inne informacje o pliku i zmiennej powodującej błąd w systemie.

Klasy to typy reprezentujące obiekty, które mogą mieć właściwości, metody i zdarzenia. „Służą do modelowania działań, procesów i wszelkich bytów pojęciowych w aplikacjach”.

Składnia

Składnia definiowania typu klasy jest następująca -

// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
   [ class ]
      [ inherit base-type-name(base-constructor-args) ]
      [ let-bindings ]
      [ do-bindings ]
      member-list
      ...
   [ end ]

// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...

Gdzie,

  • Plik type-nameto dowolny prawidłowy identyfikator. Domyślnym modyfikatorem dostępu jestpublic.

  • Plik type-params opisuje opcjonalne parametry typu ogólnego.

  • Plik parameter-listopisuje parametry konstruktora. Domyślnym modyfikatorem dostępu dla głównego konstruktora jestpublic.

  • Plik identifier używany z opcjonalnym as słowo kluczowe nadaje nazwę zmiennej instancji lub self-identifier, których można użyć w definicji typu, aby odwołać się do wystąpienia typu.

  • Plik inherit słowo kluczowe umożliwia określenie klasy bazowej dla klasy.

  • Plik let powiązania umożliwiają deklarowanie pól lub wartości funkcji lokalnie dla klasy.

  • Plik do-bindings sekcja zawiera kod, który ma być wykonany podczas budowy obiektu.

  • Plik member-list składa się z dodatkowych konstruktorów, deklaracji instancji i metod statycznych, deklaracji interfejsów, powiązań abstrakcyjnych oraz deklaracji właściwości i zdarzeń.

  • Słowa kluczowe class i end zaznaczające początek i koniec definicji są opcjonalne.

Konstruktor klasy

Konstruktor to kod, który tworzy instancję typu klasy.

W języku F # konstruktory działają trochę inaczej niż inne języki .Net. W definicji klasy argumenty konstruktora podstawowego są opisane jako lista-parametrów.

Ciało konstruktora składa się z let i do wiązania.

Możesz dodać dodatkowe konstruktory, używając słowa kluczowego new, aby dodać członka -

new (argument-list) = constructor-body

Poniższy przykład ilustruje koncepcję -

Przykład

Poniższy program tworzy klasę linii wraz z konstruktorem, który oblicza długość linii podczas tworzenia obiektu klasy -

type Line = class
   val X1 : float
   val Y1 : float
   val X2 : float
   val Y2 : float

   new (x1, y1, x2, y2) as this =
      { X1 = x1; Y1 = y1; X2 = x2; Y2 = y2;}
      then
         printfn " Creating Line: {(%g, %g), (%g, %g)}\nLength: %g"
            this.X1 this.Y1 this.X2 this.Y2 this.Length

   member x.Length =
      let sqr x = x * x
      sqrt(sqr(x.X1 - x.X2) + sqr(x.Y1 - x.Y2) )
end
let aLine = new Line(1.0, 1.0, 4.0, 5.0)

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Creating Line: {(1, 1), (4, 5)}
Length: 5

Niech Bindings

Powiązania let w definicji klasy umożliwiają definiowanie pól prywatnych i funkcji prywatnych dla klas F #.

type Greetings(name) as gr =
   let data = name
   do
      gr.PrintMessage()
   member this.PrintMessage() =
      printf "Hello %s\n" data
let gtr = new Greetings("Zara")

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Hello Zara

Zwróć uwagę na użycie własnego identyfikatora gr dla klasy Greetings .

Struktura w F # jest typem danych typu wartości. Pomaga tworzyć pojedynczą zmienną, przechowywać powiązane dane różnych typów danych. Plikstruct słowo kluczowe służy do tworzenia struktury.

Składnia

Składnia definiowania struktury jest następująca -

[ attributes ]
type [accessibility-modifier] type-name =
   struct
      type-definition-elements
   end
// or
[ attributes ]
[<StructAttribute>]
type [accessibility-modifier] type-name =
   type-definition-elements

Istnieją dwie składnie. Najczęściej używana jest pierwsza składnia, ponieważ jeśli używaszstruct i end słowa kluczowe, możesz pominąć StructAttribute atrybut.

Elementy definicji konstrukcji zapewniają -

  • Deklaracje i definicje członków.
  • Konstruktory oraz zmienne i niezmienne pola.
  • Członkowie i implementacje interfejsów.

W przeciwieństwie do klas, struktury nie mogą być dziedziczone i nie mogą zawierać powiązań let lub do. Ponieważ struktury nie pozwalają na powiązania; musisz zadeklarować pola w strukturach przy użyciuval słowo kluczowe.

Podczas definiowania pola i jego typu za pomocą valsłowo kluczowe, nie można zainicjować wartości pola, zamiast tego są one inicjowane na zero lub null. Tak więc w przypadku struktury mającej niejawny konstruktor,val deklaracje należy opatrzyć adnotacją DefaultValue atrybut.

Przykład

Poniższy program tworzy strukturę liniową wraz z konstruktorem. Program oblicza długość linii na podstawie struktury -

type Line = struct
   val X1 : float
   val Y1 : float
   val X2 : float
   val Y2 : float

   new (x1, y1, x2, y2) =
      {X1 = x1; Y1 = y1; X2 = x2; Y2 = y2;}
end
let calcLength(a : Line)=
   let sqr a = a * a
   sqrt(sqr(a.X1 - a.X2) + sqr(a.Y1 - a.Y2) )

let aLine = new Line(1.0, 1.0, 4.0, 5.0)
let length = calcLength aLine
printfn "Length of the Line: %g " length

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Length of the Line: 5

Możesz ponownie zdefiniować lub przeciążać większość wbudowanych operatorów dostępnych w języku F #. W ten sposób programista może również używać operatorów z typami zdefiniowanymi przez użytkownika.

Operatory to funkcje o specjalnych nazwach, umieszczonych w nawiasach. Muszą być zdefiniowane jako statyczne elementy klasy. Podobnie jak każda inna funkcja, przeciążony operator ma typ zwracany i listę parametrów.

Poniższy przykład przedstawia operator + na liczbach zespolonych -

//overloading + operator
static member (+) (a : Complex, b: Complex) =
Complex(a.x + b.x, a.y + b.y)

Powyższa funkcja implementuje operator dodawania (+) dla klasy Complex zdefiniowanej przez użytkownika. Dodaje atrybuty dwóch obiektów i zwraca wynikowy obiekt Complex.

Implementacja przeciążania operatorów

Poniższy program przedstawia pełną realizację -

//implementing a complex class with +, and - operators
//overloaded
type Complex(x: float, y : float) =
   member this.x = x
   member this.y = y
   //overloading + operator
   static member (+) (a : Complex, b: Complex) =
      Complex(a.x + b.x, a.y + b.y)

   //overloading - operator
   static member (-) (a : Complex, b: Complex) =
      Complex(a.x - b.x, a.y - b.y)

   // overriding the ToString method
   override this.ToString() =
      this.x.ToString() + " " + this.y.ToString()

//Creating two complex numbers
let c1 = Complex(7.0, 5.0)
let c2 = Complex(4.2, 3.1)

// addition and subtraction using the
//overloaded operators
let c3 = c1 + c2
let c4 = c1 - c2

//printing the complex numbers
printfn "%s" (c1.ToString())
printfn "%s" (c2.ToString())
printfn "%s" (c3.ToString())
printfn "%s" (c4.ToString())

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

7 5
4.2 3.1
11.2 8.1
2.8 1.9

Jedną z najważniejszych koncepcji programowania obiektowego jest dziedziczenie. Dziedziczenie pozwala nam zdefiniować klasę pod względem innej klasy, co ułatwia tworzenie i utrzymywanie aplikacji. Daje to również możliwość ponownego wykorzystania funkcjonalności kodu i szybkiego czasu implementacji.

Tworząc klasę, zamiast pisać zupełnie nowe składowe danych i funkcje składowe, programista może wyznaczyć, że nowa klasa powinna dziedziczyć składowe istniejącej klasy. Ta istniejąca klasa jest nazywana klasą bazową, a nowa klasa jest nazywana klasą pochodną.

Idea dziedziczenia implementuje relację IS-A. Na przykład, ssak JEST zwierzęciem, pies IS-A ssak, a więc pies również jest zwierzęciem IS-A i tak dalej.

Klasa podstawowa i podklasa

Podklasa pochodzi z klasy bazowej, która jest już zdefiniowana. Podklasa dziedziczy członków klasy bazowej, a także ma swoich własnych członków.

Podklasa jest definiowana przy użyciu inherit słowo kluczowe, jak pokazano poniżej -

type MyDerived(...) =
   inherit MyBase(...)

W języku F # klasa może mieć co najwyżej jedną bezpośrednią klasę bazową. Jeśli nie określisz klasy bazowej przy użyciuinherit słowo kluczowe, klasa dziedziczy niejawnie po Object.

Uwaga -

  • Metody i elementy członkowskie klasy bazowej są dostępne dla użytkowników klasy pochodnej, podobnie jak bezpośredni członkowie klasy pochodnej.

  • Niech powiązania i parametry konstruktora są prywatne dla klasy i dlatego nie można uzyskać do nich dostępu z klas pochodnych.

  • Słowo kluczowe baseodnosi się do wystąpienia klasy bazowej. Jest używany jako własny identyfikator.

Przykład

type Person(name) =
   member x.Name = name
   member x.Greet() = printfn "Hi, I'm %s" x.Name

type Student(name, studentID : int) =
   inherit Person(name)
   let mutable _GPA = 0.0
   member x.StudentID = studentID
   member x.GPA
      with get() = _GPA
      and set value = _GPA <- value

type Teacher(name, expertise : string) =
   inherit Person(name)

   let mutable _salary = 0.0
   member x.Salary
      with get() = _salary
      and set value = _salary <- value
   member x.Expertise = expertise

//using the subclasses
let p = new Person("Mohan")
let st = new Student("Zara", 1234)
let tr = new Teacher("Mariam", "Java")

p.Greet()
st.Greet()
tr.Greet()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Hi, I'm Mohan
Hi, I'm Zara
Hi, I'm Mariam

Metody zastępujące

Można przesłonić domyślne zachowanie metody klasy bazowej i zaimplementować je inaczej w podklasie lub klasie pochodnej.

Metody w języku F # nie są domyślnie zastępowalne.

Aby przesłonić metody w klasie pochodnej, musisz zadeklarować swoją metodę jako możliwą do zastąpienia przy użyciu abstract i default słowa kluczowe w następujący sposób -

type Person(name) =
   member x.Name = name
   abstract Greet : unit -> unit
   default x.Greet() = printfn "Hi, I'm %s" x.Name

Teraz metodę Greet klasy Person można zastąpić w klasach pochodnych. Poniższy przykład demonstruje to -

Przykład

type Person(name) =
   member x.Name = name
   abstract Greet : unit -> unit
   default x.Greet() = printfn "Hi, I'm %s" x.Name

type Student(name, studentID : int) =
   inherit Person(name)

   let mutable _GPA = 0.0

   member x.StudentID = studentID
   member x.GPA
      with get() = _GPA
      and set value = _GPA <- value
   override x.Greet() = printfn "Student %s" x.Name

type Teacher(name, expertise : string) =
   inherit Person(name)
   let mutable _salary = 0.0
   member x.Salary
      with get() = _salary
      and set value = _salary <- value

   member x.Expertise = expertise
   override x.Greet() = printfn "Teacher %s." x.Name

//using the subclasses
let p = new Person("Mohan")
let st = new Student("Zara", 1234)
let tr = new Teacher("Mariam", "Java")

//default Greet
p.Greet()

//Overriden Greet
st.Greet()
tr.Greet()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Hi, I'm Mohan
Student Zara
Teacher Mariam.

Klasa abstrakcyjna

Czasami trzeba zapewnić niepełną implementację obiektu, który w rzeczywistości nie powinien być implementowany. Później jakiś inny programista powinien utworzyć podklasy klasy abstrakcyjnej do pełnej implementacji.

Na przykład klasa Person nie będzie potrzebna w systemie zarządzania szkołą. Jednak klasa ucznia lub nauczyciela będzie potrzebna. W takich przypadkach można zadeklarować klasę Person jako klasę abstrakcyjną.

Plik AbstractClass atrybut mówi kompilatorowi, że klasa ma kilka abstrakcyjnych członków.

Nie można utworzyć wystąpienia klasy abstrakcyjnej, ponieważ klasa ta nie jest w pełni zaimplementowana.

Poniższy przykład demonstruje to -

Przykład

[<AbstractClass>]
type Person(name) =
   member x.Name = name
   abstract Greet : unit -> unit

type Student(name, studentID : int) =
   inherit Person(name)
   let mutable _GPA = 0.0
   member x.StudentID = studentID
   member x.GPA
      with get() = _GPA
      and set value = _GPA <- value
   override x.Greet() = printfn "Student %s" x.Name

type Teacher(name, expertise : string) =
   inherit Person(name)
   let mutable _salary = 0.0
   member x.Salary
      with get() = _salary
      and set value = _salary <- value
   member x.Expertise = expertise
   override x.Greet() = printfn "Teacher %s." x.Name

let st = new Student("Zara", 1234)
let tr = new Teacher("Mariam", "Java")

//Overriden Greet
st.Greet()
tr.Greet()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Student Zara
Teacher Mariam.

Interfejsy zapewniają abstrakcyjny sposób zapisywania szczegółów implementacji klasy. Jest to szablon, który deklaruje metody, które klasa musi zaimplementować i ujawnić publicznie.

Składnia

Interfejs określa zestawy powiązanych elementów, które implementują inne klasy. Ma następującą składnię -

// Interface declaration:
[ attributes ]
type interface-name =
   [ interface ]
      [ inherit base-interface-name ...]
      abstract member1 : [ argument-types1 -> ] return-type1
      abstract member2 : [ argument-types2 -> ] return-type2
      ...
   [ end ]
	
// Implementing, inside a class type definition:
interface interface-name with
   member self-identifier.member1 argument-list = method-body1
   member self-identifier.member2 argument-list = method-body2
// Implementing, by using an object expression:
[ attributes ]
let class-name (argument-list) =
   { new interface-name with
      member self-identifier.member1 argument-list = method-body1
      member self-identifier.member2 argument-list = method-body2
      [ base-interface-definitions ]
   }
member-list

Uwaga -

  • W deklaracji interfejsu członkowie nie są zaimplementowani.

  • Członkowie są abstrakcyjni, zadeklarowani przez abstractsłowo kluczowe. Możesz jednak podać domyślną implementację przy użyciudefault słowo kluczowe.

  • Interfejsy można zaimplementować za pomocą wyrażeń obiektów lub typów klas.

  • W implementacji klas lub obiektów musisz dostarczyć treści metod dla abstrakcyjnych metod interfejsu.

  • Słowa kluczowe interface i end, które oznaczają początek i koniec definicji, są opcjonalne.

Na przykład,

type IPerson =
   abstract Name : string
   abstract Enter : unit -> unit
   abstract Leave : unit -> unit

Wywołanie metod interfejsu

Metody interfejsu są wywoływane za pośrednictwem interfejsu, a nie przez wystąpienie klasy lub typu implementującego interfejs. Aby wywołać metodę interfejsu, należy przesłać rzutowanie do typu interfejsu przy użyciu:> operator (operator upcast).

Na przykład,

(s :> IPerson).Enter()
(s :> IPerson).Leave()

Poniższy przykład ilustruje koncepcję -

Przykład

type IPerson =
   abstract Name : string
   abstract Enter : unit -> unit
   abstract Leave : unit -> unit

type Student(name : string, id : int) =
   member this.ID = id
   interface IPerson with
      member this.Name = name
      member this.Enter() = printfn "Student entering premises!"
      member this.Leave() = printfn "Student leaving premises!"

type StuffMember(name : string, id : int, salary : float) =
   let mutable _salary = salary

   member this.Salary
      with get() = _salary
      and set(value) = _salary <- value

   interface IPerson with
      member this.Name = name
      member this.Enter() = printfn "Stuff member entering premises!"
      member this.Leave() = printfn "Stuff member leaving premises!"

let s = new Student("Zara", 1234)
let st = new StuffMember("Rohit", 34, 50000.0)

(s :> IPerson).Enter()
(s :> IPerson).Leave()
(st :> IPerson).Enter()
(st :> IPerson).Leave()

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Student entering premises!
Student leaving premises!
Stuff member entering premises!
Stuff member leaving premises!

Dziedziczenie interfejsu

Interfejsy mogą dziedziczyć z jednego lub więcej interfejsów podstawowych.

Poniższy przykład przedstawia koncepcję -

type Interface1 =
   abstract member doubleIt: int -> int

type Interface2 =
   abstract member tripleIt: int -> int

type Interface3 =
   inherit Interface1
   inherit Interface2
   abstract member printIt: int -> string

type multiplierClass() =
   interface Interface3 with
      member this.doubleIt(a) = 2 * a
      member this.tripleIt(a) = 3 * a
      member this.printIt(a) = a.ToString()

let ml = multiplierClass()
printfn "%d" ((ml:>Interface3).doubleIt(5))
printfn "%d" ((ml:>Interface3).tripleIt(5))
printfn "%s" ((ml:>Interface3).printIt(5))

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

10
15
5

Zdarzenia umożliwiają klasom wysyłanie i odbieranie wiadomości między sobą.

W graficznym interfejsie użytkownika zdarzenia to działania użytkownika, takie jak naciśnięcie klawisza, kliknięcia, ruchy myszy itp. Lub pewne zdarzenia, takie jak powiadomienia generowane przez system. Aplikacje muszą reagować na pojawiające się zdarzenia. Na przykład przerywa. Zdarzenia służą do komunikacji między procesami.

Obiekty komunikują się ze sobą poprzez synchroniczne przekazywanie wiadomości.

Wydarzenia są powiązane z innymi funkcjami; rejestr obiektówcallback funkcje do zdarzenia, a te wywołania zwrotne są wykonywane, gdy (i jeśli) zdarzenie jest wyzwalane przez jakiś obiekt.

Klasa zdarzenia i moduł zdarzeń

Klasa Control.Event <'T> pomaga w tworzeniu obserwowalnego obiektu lub zdarzenia.

Ma następujących członków instancji do pracy ze zdarzeniami:

Członek Opis
Publikować Publikuje obserwację jako wartość pierwszej klasy.
Cyngiel Uruchamia obserwację przy użyciu podanych parametrów.

Moduł Control.Event zapewnia funkcje do zarządzania strumieniami zdarzeń -

Wartość Opis
dodaj: ('T → jednostka) → Zdarzenie <' Del, 'T> → jednostka Uruchamia daną funkcję za każdym razem, gdy zostanie wyzwolone dane zdarzenie.
wybierz: (opcja 'T →' U) → IEvent <'Del,' T> → IEvent <'U> Zwraca nowe zdarzenie, które jest uruchamiane na wybranych wiadomościach z pierwotnego zdarzenia. Funkcja wyboru przenosi oryginalną wiadomość do opcjonalnej nowej wiadomości.
filter: ('T → bool) → IEvent <' Del, 'T> → IEvent <' T> Zwraca nowe zdarzenie, które nasłuchuje pierwotnego zdarzenia i wyzwala wynikowe zdarzenie tylko wtedy, gdy argument zdarzenia przekazuje daną funkcję.
mapa: ('T →' U) → IEvent <'Del,' T> → IEvent <'U> Zwraca nowe zdarzenie, które przekazuje wartości przekształcone przez daną funkcję.
scal: IEvent <'Del1,' T> → IEvent <'Del2,' T> → IEvent <'T> Uruchamia zdarzenie wyjściowe, gdy jedno ze zdarzeń wejściowych jest uruchamiane.
parami: IEvent <'Del,' T> → IEvent <'T *' T> Zwraca nowe zdarzenie, które jest wyzwalane przy drugim i kolejnym wyzwoleniu zdarzenia wejściowego. PlikNth wyzwolenie zdarzenia wejściowego powoduje przekazanie argumentów z N-1th i Nthwyzwalanie jako para. Argument przekazany doN-1th wyzwalanie jest utrzymywane w ukrytym stanie wewnętrznym do czasu Nth następuje wyzwalanie.
partycja: ('T → bool) → IEvent <' Del, 'T> → IEvent <' T> * IEvent <'T> Zwraca nowe zdarzenie, które nasłuchuje pierwotnego zdarzenia i wyzwala pierwsze zdarzenie wynikowe, jeśli zastosowanie predykatu do argumentów zdarzenia zwróciło wartość true, a drugie zdarzenie, jeśli zwróciło wartość false.
skanowanie: ('U →' T → 'U) →' U → IEvent <'Del,' T> → IEvent <'U> Zwraca nowe zdarzenie składające się z wyników zastosowania danej funkcji sumującej do kolejnych wartości wyzwalanych w zdarzeniu wejściowym. Pozycja stanu wewnętrznego rejestruje bieżącą wartość parametru stanu. Stan wewnętrzny nie jest blokowany podczas wykonywania funkcji akumulacji, dlatego należy uważać, aby wejście IEvent nie było wyzwalane przez wiele wątków jednocześnie.
split: ('T → Wybór <' U1, 'U2>) → IEvent <' Del, 'T> → IEvent <' U1> * IEvent <'U2> Zwraca nowe zdarzenie, które nasłuchuje oryginalnego zdarzenia i wyzwala pierwsze zdarzenie wynikowe, jeśli zastosowanie funkcji do argumentów zdarzenia zwróciło Choice1Of2, a drugie zdarzenie, jeśli zwróci Choice2Of2.

Tworzenie wydarzeń

Zdarzenia są tworzone i używane za pośrednictwem Eventklasa. Konstruktor Event służy do tworzenia zdarzenia.

Przykład

type Worker(name : string, shift : string) =
   let mutable _name = name;
   let mutable _shift = shift;
   let nameChanged = new Event<unit>() (* creates event *)
   let shiftChanged = new Event<unit>() (* creates event *)

   member this.Name
      with get() = _name
      and set(value) = _name <- value

   member this.Shift
      with get() = _shift
      and set(value) = _shift <- value

Następnie musisz ujawnić pole nameChanged jako publiczny element członkowski, aby nasłuchujący mogli podłączyć się do zdarzenia, dla którego używasz Publish własność imprezy -

type Worker(name : string, shift : string) =
   let mutable _name = name;
   let mutable _shift = shift;

   let nameChanged = new Event<unit>() (* creates event *)
   let shiftChanged = new Event<unit>() (* creates event *)

   member this.NameChanged = nameChanged.Publish (* exposed event handler *)
   member this.ShiftChanged = shiftChanged.Publish (* exposed event handler *)

   member this.Name
      with get() = _name
      and set(value) = _name <- value
      nameChanged.Trigger() (* invokes event handler *)

   member this.Shift
      with get() = _shift
      and set(value) = _shift <- value
   shiftChanged.Trigger() (* invokes event handler *)

Następnie dodajesz wywołania zwrotne do programów obsługi zdarzeń. Każdy program obsługi zdarzeń ma typ IEvent <'T>, który udostępnia kilka metod -

metoda Opis
val Dodaj: zdarzenie :( 'T → jednostka) → jednostka Łączy funkcję detektora ze zdarzeniem. Odbiornik zostanie wywołany po uruchomieniu zdarzenia.
val AddHandler: 'del → unit Łączy obiekt delegata programu obsługi ze zdarzeniem. Program obsługi można później usunąć za pomocą RemoveHandler. Odbiornik zostanie wywołany po uruchomieniu zdarzenia.
val RemoveHandler: 'del → unit Usuwa delegata nasłuchiwania z magazynu nasłuchiwania zdarzeń.

Poniższa sekcja zawiera pełny przykład.

Przykład

Poniższy przykład demonstruje koncepcję i techniki omówione powyżej -

type Worker(name : string, shift : string) =
   let mutable _name = name;
   let mutable _shift = shift;

   let nameChanged = new Event<unit>() (* creates event *)
   let shiftChanged = new Event<unit>() (* creates event *)

   member this.NameChanged = nameChanged.Publish (* exposed event handler *)
   member this.ShiftChanged = shiftChanged.Publish (* exposed event handler *)

   member this.Name
      with get() = _name
      and set(value) = 
         _name <- value
         nameChanged.Trigger() (* invokes event handler *)

   member this.Shift
      with get() = _shift
      and set(value) = 
         _shift <- value
         shiftChanged.Trigger() (* invokes event handler *)

let wk = new Worker("Wilson", "Evening")
wk.NameChanged.Add(fun () -> printfn "Worker changed name! New name: %s" wk.Name)
wk.Name <- "William"
wk.NameChanged.Add(fun () -> printfn "-- Another handler attached to NameChanged!")
wk.Name <- "Bill"

wk.ShiftChanged.Add(fun () -> printfn "Worker changed shift! New shift: %s" wk.Shift)
wk.Shift <- "Morning"
wk.ShiftChanged.Add(fun () -> printfn "-- Another handler attached to ShiftChanged!")
wk.Shift <- "Night"

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Worker changed name! New name: William
Worker changed name! New name: Bill
-- Another handler attached to NameChanged!
Worker changed shift! New shift: Morning
Worker changed shift! New shift: Night
-- Another handler attached to ShiftChanged!

Zgodnie z biblioteką MSDN moduł F # to grupa konstrukcji kodu F #, takich jak typy, wartości, wartości funkcji i kod w powiązaniach do. Jest implementowana jako klasa środowiska uruchomieniowego języka wspólnego (CLR), która ma tylko statyczne elementy członkowskie.

W zależności od sytuacji, czy cały plik jest zawarty w module, istnieją dwa rodzaje deklaracji modułu -

  • Deklaracja modułu najwyższego poziomu
  • Deklaracja modułu lokalnego

W deklaracji modułu najwyższego poziomu cały plik jest zawarty w module. W tym przypadku pierwszą deklaracją w pliku jest deklaracja modułu. Nie musisz wciskać deklaracji w module najwyższego poziomu.

W deklaracji modułu lokalnego tylko deklaracje, które są wcięte w tej deklaracji modułu, są częścią modułu.

Składnia

Składnia deklaracji modułu jest następująca -

// Top-level module declaration.
module [accessibility-modifier] [qualified-namespace.]module-name
   declarations
// Local module declaration.
module [accessibility-modifier] module-name =
   declarations

Należy pamiętać, że modyfikatorem dostępności może być jeden z następujących - publiczny, prywatny, wewnętrzny. Wartość domyślna topublic.

Poniższe przykłady zademonstrują koncepcje -

Przykład 1

Plik modułu Arithmetic.fs -

module Arithmetic
let add x y =
   x + y

let sub x y =
   x - y
	
let mult x y =
   x * y
	
let div x y =
   x / y

Plik programu main.fs -

// Fully qualify the function name.
open Arithmetic
let addRes = Arithmetic.add 25 9
let subRes = Arithmetic.sub 25 9
let multRes = Arithmetic.mult 25 9
let divRes = Arithmetic.div 25 9

printfn "%d" addRes
printfn "%d" subRes
printfn "%d" multRes
printfn "%d" divRes

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

34
16
225
2
110
90
1000
10

Przykład 2

// Module1
module module1 =
   // Indent all program elements within modules that are declared with an equal sign.
   let value1 = 100
   let module1Function x =
      x + value1

// Module2
module module2 =
   let value2 = 200

   // Use a qualified name to access the function.
   // from module1.
   let module2Function x =
      x + (module1.module1Function value2)

let result = module1.module1Function 25
printfn "%d" result

let result2 = module2.module2Function 25
printfn "%d" result2

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

125
325

ZA namespacema na celu zapewnienie sposobu na oddzielenie jednego zestawu nazw od drugiego. Nazwy klas zadeklarowane w jednej przestrzeni nazw nie będą kolidować z tymi samymi nazwami klas zadeklarowanymi w innej.

Zgodnie z biblioteką MSDN, plik namespace pozwala uporządkować kod w obszary powiązanych funkcji, umożliwiając dołączenie nazwy do grupy elementów programu.

Deklarowanie przestrzeni nazw

Aby zorganizować kod w przestrzeni nazw, musisz zadeklarować przestrzeń nazw jako pierwszą deklarację w pliku. Zawartość całego pliku staje się wtedy częścią przestrzeni nazw.

namespace [parent-namespaces.]identifier

Poniższy przykład ilustruje koncepcję -

Przykład

namespace testing

module testmodule1 =
   let testFunction x y =
      printfn "Values from Module1: %A %A" x y
module testmodule2 =
   let testFunction x y =
      printfn "Values from Module2: %A %A" x y

module usermodule =
   do
      testmodule1.testFunction ( "one", "two", "three" ) 150
      testmodule2.testFunction (seq { for i in 1 .. 10 do yield i * i }) 200

Kiedy kompilujesz i wykonujesz program, daje on następujący wynik -

Values from Module1: ("one", "two", "three") 150
Values from Module2: seq [1; 4; 9; 16; ...] 200