Home

Structures

 

Fundamentals of Structures

 

Introduction

A structure is a list of characteristics that describe a small object. A structure itself provides words, as members or fields, that would be used to create an object.

Creating a Structure

The basic formula to create a structure is:

type StructureName =
    struct
	Members
    end

As seen for classes, the creation of a structure starts with the type keyword followed by a name and =. The body of a structure starts with the struct keyword and ends with the end keyword. In reality, you always ought to indicate that you are creating a structure. The struct and the end keywords are an optional combination. This means that if you use struct, then you must also end the structure with the end keyword. An alternative is that, if you omit the struct and the end keywords, then you must start the structure with the [<StructAttribute>] attribute. The formula to use becomes:

[<StructAttribute>]
type StructureName =
    Members

The body of the structure contains its members.

The Fields of a Structure

The member variables of a structure are used to carry the values of the structure. They are called fields and they are created using the val keyword. The formula to create a field is:

val FieldName: data-type[;]

A field is created using the val keyword, a name, a colon, and the desired data type. The ending semicolon is optional. Here are examples:

type Course =
    struct
        val CourseCode : string;
        val CourseName : string;
        val Credits : int;
    end

Creating and Using an Object From a Structure

To create an object from a structure, declare a variable and assign an instance of the structure to it. Here is an example:

type Course =
    struct
        val CourseCode : string;
        val CourseName : string;
        val Credits : int;
    end

let crs = Course()

You can also use the new operator to allocate memory for the variable. Here is an example:

type Course =
    struct
        val CourseCode : string;
        val CourseName : string;
        val Credits : int;
    end

let crs = new Course()

If you are planning to change something about the structural object, declare its variable as mutable. If you are planning to set or change the value of a field outside the structure, you should create it as mutable. Here are examples:

type Course =
    struct
        val mutable CourseCode : string;
        val mutable CourseName : string;
        val mutable Credits : int;
    end

To access a field outside the structure, apply a period to the object followed by the name of  the field. To initialize a member of a structural object, after creating the field as mutable, use the <- operator to assign the desired value to it. Here are examples:

open System
open System.Windows.Forms

type Course =
    struct
        val mutable CourseCode : string;
        val mutable CourseName : string;
        val mutable Credits : int;
    end

let mutable crs = Course()
crs.CourseCode <- "CMSC 140"
crs.CourseName <- "Introduction to F# Programming"
crs.Credits <- 3

// Form: School Catalogue
let school = new Form()
school.Width  <- 280
school.Height <- 138
school.Text <- "School Catalogue"

// Label: Course Code
let lblCourseCode = new Label()
lblCourseCode.Left   <- 18
lblCourseCode.Top    <- 19
lblCourseCode.Width  <- 76
lblCourseCode.Text   <- "Course Code:"
school.Controls.Add lblCourseCode

// Text Box: Course Code
let txtCourseCode = new TextBox()
txtCourseCode.Left  <- 100
txtCourseCode.Top   <- 16
txtCourseCode.Width <- 64
txtCourseCode.Text  <- crs.CourseCode
school.Controls.Add txtCourseCode

// Label: Course Name
let lblCourseName = new Label()
lblCourseName.Left <- 18
lblCourseName.Top <- 48
lblCourseName.Width <- 80
lblCourseName.Text <- "Course Name:"
school.Controls.Add lblCourseName

// Text Box: Course Name
let txtCourseName = new TextBox()
txtCourseName.Left  <- 100
txtCourseName.Top   <- 43
txtCourseName.Width <- 160
txtCourseName.Text  <- crs.CourseName
school.Controls.Add txtCourseName

// Label: Credits
let lblCredits = new Label()
lblCredits.Left  <- 18
lblCredits.Top   <- 78
lblCredits.Width <- 60
lblCredits.Text  <- "Credits:"
school.Controls.Add lblCredits

// Text Box: Credits
let txtCredits = new TextBox()
txtCredits.Left  <- 100
txtCredits.Top   <- 75
txtCredits.Width <- 64
txtCredits.Text  <- string crs.Credits
school.Controls.Add txtCredits

// Button: Close
let btnClose = new Button()
btnClose.Left <- 184
btnClose.Top  <- 74
btnClose.Text <- "Close"
let btnCloseClick e = school.Close()
btnClose.Click.Add btnCloseClick
school.Controls.Add btnClose

do Application.Run school

This would produce:

Creating and Using an Object From a Structure

     
 

The Constructors of a Structure

 

Introduction

Like a class, a structure can have a constructor, but the rules are different. After creating the fields of a structure, if you want to be able to initialize an object using the name of the structure, you should add a constructor using the new keyword. Such a constructor should include all the regular members of the structure. You can then use that constructor to create an object that is initialized. Here is an example:

open System
open System.Windows.Forms

type Employee =
    struct
        val EmployeeNumber : string
        val FirstName      : string
        val LastName       : string
        val HourlySalary   : double
        new(nbr, fName, lName, salary) = { EmployeeNumber = nbr; FirstName = fName; LastName = lName; HourlySalary = salary }
    end

let empl = Employee("8022-3840", "Mark", "Plant", 24.55)

// Form: Convenience Store
let convenienceStore = new Form()
convenienceStore.Width  <- 200
convenienceStore.Height <- 155
convenienceStore.Text <- "Convenience Store"

// Label: Employee Number
let lblEmployeeNumber = new Label()
lblEmployeeNumber.Left   <- 18
lblEmployeeNumber.Top    <- 18
lblEmployeeNumber.Width  <- 80
lblEmployeeNumber.Text   <- "Employee #:"
convenienceStore.Controls.Add lblEmployeeNumber

// Text Box: Employee Number
let txtEmployeeNumber = new TextBox()
txtEmployeeNumber.Left  <- 100
txtEmployeeNumber.Top   <- 15
txtEmployeeNumber.Width <- 80
txtEmployeeNumber.Text  <- empl.EmployeeNumber
convenienceStore.Controls.Add txtEmployeeNumber

// Label: First Name
let lblFirstName = new Label()
lblFirstName.Left <- 18
lblFirstName.Top <- 44
lblFirstName.Width <- 66
lblFirstName.Text <- "First Name:"
convenienceStore.Controls.Add lblFirstName

// Text Box: First Name
let txtFirstName = new TextBox()
txtFirstName.Left  <- 100
txtFirstName.Top   <- 41
txtFirstName.Width <- 80
txtFirstName.Text  <- empl.FirstName
convenienceStore.Controls.Add txtFirstName

// Label: Last Name
let lblLastName = new Label()
lblLastName.Left  <- 18
lblLastName.Top   <- 70
lblLastName.Width <- 66
lblLastName.Text  <- "Last Name:"
convenienceStore.Controls.Add lblLastName

// Text Box: Last Name
let txtLastName = new TextBox()
txtLastName.Left  <- 100
txtLastName.Top   <-  67
txtLastName.Width <-  80
txtLastName.Text  <- string empl.LastName
convenienceStore.Controls.Add txtLastName

// Label: Hourly Salary
let lblHourlySalary = new Label()
lblHourlySalary.Left  <- 18
lblHourlySalary.Top   <- 96
lblHourlySalary.Width <- 80
lblHourlySalary.Text  <- "Hourly Salary:"
convenienceStore.Controls.Add lblHourlySalary

// Text Box: Hourly Salary
let txtHourlySalary = new TextBox()
txtHourlySalary.Left  <- 100
txtHourlySalary.Top   <-  93
txtHourlySalary.Width <-  80
txtHourlySalary.Text  <- string empl.HourlySalary
convenienceStore.Controls.Add txtHourlySalary

do Application.Run convenienceStore

This would produce:

The Constructors of a Structure

Structures and the Primary Constructor

Instead of a new constructor, you can create a primary constructor on the name of the structure.  Here is an example:

type Course(code : string, name : string, credit : int) =
    struct
        val mutable CourseCode : string;
        val mutable CourseName : string;
        val mutable Credits : int;
    end

If you create a primary constructor, you must provide a default value for each member. To do this, each member must be marked with the [<DefaultValue>] attribute. Here are examples:

type Course(code : string, name : string, credit : int) =
    struct
        [<DefaultValue>]
        val mutable CourseCode : string;
        [<DefaultValue>]
        val mutable CourseName : string;
        [<DefaultValue>]
        val mutable Credits : int;
    end

Built-In Structures

   

.NET Data Types

So far, we have used data types as they are available in the F# language. The .NET Framework provides equivalent data types as they are supported by other .NET Languages such as C# or Visual Basic. The corresponding list of types is as follows:

CharacterF# Type .NET Structure
char Char
string String
byte Byte
sbyte SByte
int16 Int16
uint16 UInt16
int Int32
uint32 UInt32
int64 Int64
uint64 UInt64
nativeint IntPtr
unativeint UIntPtr
float32, single Single
float, double Double
decimal Decimal

The data types in the .NET Framework are available as structures with properties and methods. Those structures are available from the System namespace. . As you know that there are differences among data types, some properties and methods are shared by all data types but some characteristcs are unique to some types.

The .NET types support the same operations and have the same characteristics of the types in F#. For example, all types support the addition (+) operation. All number-based types support the subtraction (-), the multiplication (*), and the division (/).

All types support comparisons between values of the same type. The simplest comparison is meant to find out whether two values are equal. The types support this with the = operator and/or a method named Equals. On the other hand, you may want to find out whether one value is lower or higher than another. Such a comparison is done using a method named CompareTo. This method takes one argument that is the same type as the variable that is calling it. The method returns an int value. For example, imagine you have two int variables named variable1 and variable2, you can call the CompareTo() method of the first variable to compare its value to that of the second variable. This would be done as in:

let result = variable1.CompareTo variable2

The end result would be as follows:

  • If the value of Variable1 is greater than the value of Variable2, the method returns 1
  • If the value of Variable1 is less than the value of Variable2, the method returns -1
  • If the values of both variables are equal, the method returns 0

To allow you to convert any value to a string, each structure of a .NET type is equipped with a method named ToString. The easiest way to use it is to simply call it on the variable of a data type. Here is an example:

open System
open System.Windows.Forms

let allocate1 amount ratio1 ratio2 =
    let totalRatios = ratio1 + ratio2
    let eachPart = amount / totalRatios
    let part1 = eachPart * ratio1
    part1

let allocate2 amount ratio1 ratio2 =
    let totalRatios : float = ratio1 + ratio2
    let eachPart : float    = amount / totalRatios
    let part2 : float       = eachPart * ratio2
    part2

// Form: Ratio
let ratio = new Form()
ratio.Width  <- 268
ratio.Height <- 198
ratio.Text <- "Ratio"

// Label: Allocate
let lblAllocate = new Label()
lblAllocate.Left <- 22
lblAllocate.Top  <- 19
lblAllocate.Width <- 60
lblAllocate.Text <- "Allocate:"
ratio.Controls.Add lblAllocate

// Text Box: Allocate
let txtAllocate = new TextBox()
txtAllocate.Left <- 88
txtAllocate.Top <- 16
txtAllocate.Width <- 79
ratio.Controls.Add txtAllocate

// Label: In the ratio
let lblInTheRatio = new Label()
lblInTheRatio.Left <- 22
lblInTheRatio.Top <- 54
lblInTheRatio.Width <- 65
lblInTheRatio.Text <- "In the ratio:"
ratio.Controls.Add lblInTheRatio

// Text Box: Part1
let txtPart1 = new TextBox()
txtPart1.Left <- 88
txtPart1.Top <- 51
txtPart1.Width <- 31
ratio.Controls.Add txtPart1

// Label: Colon
let lblColon = new Label()
lblColon.Left <- 123
lblColon.Top <- 54
lblColon.Width <- 10
lblColon.Text <- ":"
ratio.Controls.Add lblColon

// Text Box: Part2
let txtPart2 = new TextBox()
txtPart2.Left  <- 136
txtPart2.Top   <- 51
txtPart2.Width <- 31
ratio.Controls.Add txtPart2

// Button: Allocate
let btnAllocate = new Button()
btnAllocate.Left <- 173
btnAllocate.Top  <-  49
btnAllocate.Text <- "Allocate"

// Label: Line
let lblLine = new Label()
lblLine.BackColor <- System.Drawing.Color.Black
lblLine.Left   <- 12
lblLine.Top    <- 84
lblLine.Width  <- 245
lblLine.Height <- 1
ratio.Controls.Add lblLine

// Label: Part 1 Receives
let lblPart1Receives = new Label()
lblPart1Receives.Left  <-  22
lblPart1Receives.Top   <- 102
lblPart1Receives.Width <-  90
lblPart1Receives.Text  <- "Part 1 Receives:"
ratio.Controls.Add lblPart1Receives

// Text Box: Part 1 Value
let txtPart1Value = new TextBox()
txtPart1Value.Left  <- 114
txtPart1Value.Top   <-  99
txtPart1Value.Width <-  53
ratio.Controls.Add txtPart1Value

// Label: Part 2 Receives
let lblPart2Receives = new Label()
lblPart2Receives.Left  <-  22
lblPart2Receives.Top   <- 138
lblPart2Receives.Width <-  90
lblPart2Receives.Text  <- "Part 2 Receives:"
ratio.Controls.Add lblPart2Receives

let txtPart2Value = new TextBox()
txtPart2Value.Left  <- 114
txtPart2Value.Top   <- 135
txtPart2Value.Width <-  53
ratio.Controls.Add txtPart2Value

// Button: Close
let btnClose = new Button()
btnClose.Left <- 173
btnClose.Top  <- 133
btnClose.Text <- "Close"

let btnAllocateClick e =
    let amount : float = float txtAllocate.Text
    let ratio1 : float = float txtPart1.Text
    let ratio2 : float = float txtPart2.Text
    let part1  : float  = allocate1 amount ratio1 ratio2
    let part2  : float  = allocate2 amount ratio1 ratio2

    txtPart1Value.Text <- part1.ToString()
    txtPart2Value.Text <- part2.ToString()

btnAllocate.Click.Add btnAllocateClick
ratio.Controls.Add btnAllocate

let btnCloseClick e = ratio.Close()
btnClose.Click.Add btnCloseClick
ratio.Controls.Add btnClose

do Application.Run ratio

If the value to display is numeric, you can provide a customized way for the method to display the value. To do this, use the following signature of the method:

member ToString : 
        format:string -> string

In this case, pass a string to format the value to be displayed. The string can contain a particular character that would control the format. The characters to use are as follows:

Character Displays
c C Currency values
d D Decimal numbers
e E Scientific numeric display such as 1.45e5
f F Fixed decimal numbers
d D General and most common type of numbers
n N Natural numbers
r R Roundtrip formatting
s S Hexadecimal formatting
p P Percentages

We already know that each data type has a function that makes it possible to convert a value from another type. To support this operation, each structure of a .NET type is equipped with a static method named Parse. To use it, apply the method to the .NET type and pass the desired value. When calling this method, if a bad value is passed to the Parse() method, the program produces an error. To assist you with this type of problem, each structure of a .NET type is equipped with a method named TryParse. Pass one (the first) argument as the string that contains the value to convert. Pass a second argument by reference as a Boolean value. The second argument will return true from the method if the conversion was successful. If the conversion fails, the second argument returns as false.

Each structure of a .NET type is equipped with a field that indicates its minimum value and its maximum value. These are represented respectively as MinValue and MaxValue:

static val mutable MinValue: int
static val mutable MaxValue: int

Built-In Structures: The Boolean Type

We already know that the bool data type is used to represent a value considered as being true or false. In the .NET Framework, the bool data type is represented by the Boolean structure. The true value of a bool variable is represented by the TrueString field and the false value is represented by the FalseString member variable. In other words, when true (or false) is represented as a string, "true" (or "false") is the same as TrueString (or FalseString).

To let you convert a value to bool, the Boolean structure is equipped with a static method named Parse. The Boolean.Parse() method uses the following signature:

static member Parse : 
        value:string -> bool

This method takes as argument a string. The argument must contain either the word True (case-insensitive) or the word False. If the argument is passed as "True", the method returns true. If the argument is "false", this method returns false.

When calling the Boolean.Parse() method to retrieve the value of a Boolean variable, if the supplied value is "TRUE" or "FALSE", the compiler would process it. If the value is not valid, the program would produce an error. To avoid the error, the Boolean structure provides the TryParse() method. Its signature is:

static member TryParse : 
        value:string * 
        result:bool byref -> bool

The first argument is the value to be parsed. If that value is valid, the second argument holds the true or false value of the first.

The Boolean structure supports the ability to compare two Boolean values for equality. This can be done either using the = operator or by calling the Equals() method.

The Point

To let you control the alignment of controls and to provide a convenient way to manage the coordinates of things, the .NET Framework provides a structure named Point:

type Point =
    struct
    end

The Point structure is a member of the System.Drawing namespace, which is defined in the System.Drawing.dll. Therefore, if you want to use a Point object in your application, include that assembly in your application.

One of the properties of the Point structure is named X:

member X : int with get, set

It represents the horizontal distance of the point from the top-left corner of the object that owns the point. Another property is Y:

member Y : int with get, set

It represents the vertical measurement of the point with regards to the top-left corner of the object that owns the point. Based on this, a Point object can be represented on the Windows coordinate system as follows:

Representation of a Point

As you can see, both X and Y are read-write properties. This means that you can set them individidually or get their individual values. On the other hand, to allow you to set both values as one, the Point structure is equipped with a constructor that takes two arguments for both X and Y. The signature of that constructor is:

new : 
    x:int * 
    y:int -> Point

If both coordinates have the same value, to specify them, you can use the following constructor:

new : 
    dw:int -> Point

In your applications, sometimes you will want to find out whether a point contains a value or not. To let you get this information, the Point structure is equipped with a Boolean property named IsEmpty:

member IsEmpty : bool with get

The Point structure uses integers for its values. If you want to use a point with decimal numbers, the .NET Framework provides a structure named PointF. Its is equipped with the same properties and methods as Point except that it uses values of type float.

The Size Structure

As you should know already, the width of an object is the distance from its left border to it right border and the height of an object is the distance from its top to its bottom borders. To let you manage these values as one object, the .NET Framework provides a structure named Size:

type Size =
    struct
    end

The Size structure a member of the System.Drawing namesspace defined in the System.Drawing.dll library.

To support the width value, the Size structure is equipped with a property named Width:

member Width : int with get, set

To support the height of something, the Size structure is equipped with a property named Height:

member Height : int with get, set

To let you use both the width and the height as one object, the Size structure is equipped with a constructor that takes a width and a height values. Its signature is:

new : 
    width:int * 
    height:int -> Size

As a result, you can create one Size object and use it where both the width and the height would be used. This is done by passing both values to this constructor.

The size can be illustrated as follows:

The location and dimension of a control

In most scenarios, you specify the size of an object either that has a location already, or whose location is managed by other means. Still if you want to specify the location of the size of the object, the Size structure is equipped with a constructor that takes a Point object as argument. Its signature is:

new :
    pt:Point -> Size

As a result, the size can be illustrated as follows:

Size Representation

The Size structure allows you to add one Size object to another. This can be done using the addition (+) operator, as in size1 + size2. For the same operation, the Size structure is equipped with a static mehod named Add. Its signature is:

static member Add : 
        sz1:Size * 
        sz2:Size -> Size

In both cases, the width of the second Size object would be added to the width of the first object, and the height of the second Size object would be added to the height of the second size. The Size structure also supports the subtraction operation using either - or a method named Subtract.

To let you compare two sizes for equality, the Size structure supports the equality operator (=).

The Size structure uses integral values. If you need to use decimal numbers, the .NET Framework provides the SizeF structure. It uses the same properties and methods as Size except that its members are float types.

The .NET Framework provides all types of mechanisms, using classes and methods, to convert from one size value to another.

The Rectangle Structure

The point and the size are considered as separate entities, the point as a location where something (such as an object) starts and the size that represents its dimensions. The combination of a location and size of an object is a rectangle: a geometric shape that has a location (an address) and a size (its width and its height). To assist you with operations related to a rectangle, the System.Drawing namespace provides the Rectangle structure:

type Rectangle =
    struct
    end

A rectangle can be represented as follows:

A rectangle is defined by the location and the size of an object. The location that can refer to a Point object is defined by a point on the top-left corner of the rectangle. The distance from the left border of the object that owns the rectangle to the left border of the rectangle is represented by the Left property mentioned for the Point structure. The distance from the top border of the object that owns the rectangle to the top border of the rectangle is represented by the Top property reviewed for the Point structure. The distance from the left to the right borders of the rectangle is represented by the Width property mentioned for the Size structure.

The distance from the left to the right borders of the rectangle is represented by the Height property mentioned for the Size structure. The distance from the left border of the object that owns the rectangle to the right border of the rectangle is represented by a property named Right. The distance from the top border of the object that owns the rectangle to the bottom border of the rectangle is represented by a property called Bottom. Based on this, a rectangle can be illustrated as follows:

Rectangle Representation

To create a rectangle, you must provide at least its location and dimensions. The location can be represented by a Point value and the dimensions can be represented with a Size value. To assist you with this, the Rectangle structure is equipped with the following constructor:

new :
    location:Point * 
    size:Size -> Rectangle

This constructor requires that you define a Point and a Size objects in order to use it. If you know the integer values of the location and dimensions, you can use the following constructor to declare a Rectangle object:

new :
    x:int * 
    y:int * 
    width:int * 
    height:int -> Rectangle

At any time, you can access or retrieve the characteristics of a Rectangle object as illustrated in the above picture from its properties. You use the same names we used in the illustration.

The Rectangle structure uses integral values. To let you use a rectangle with decimal values, the .NET Framework provides a structure named RectangleF. It too is defined in the System.Drawing namespace. The RectangleF structure uses the same behavior as Rectangle, except that it is defined with float values instead of integers.

   
   
 

Home Copyright © 2014-2015, FunctionX Home