Objective
Generate a Tic-Tac-Toe grid and provide a function to identify if all rows match a particular pattern.
- First let’s generate a Tic-Tac-Toe grid:let grid = [ for i in 0..8 -> (i, true) ] |> Map.ofSeqSo what’s happening here?
- We declare a value called grid that by default is immutable:
- let grid = …
- We then need to generate the actual grid structure.for i in 0..8 -> (i, true) ]
- To do this, we can generate a tuple<int, bool> nine times.
The keyword “for” is used to create a for-loop construct.
As a result, we can perform some arbitrary operation a variable number of times.
Now you might ask, so what’s the variable?
We declare the elements within our collection here:
0..8
This is shorthand for the following:
{0;1;2;3;4;5;6;7;8;}
The 0..8 syntax tells the runtime to create a set of 9 elements from 0 to 8 for our grid.
When using this shorthand within a for-loop construct, we can omit the sequence notation “{}”.
However, my brain feels better by being explicit with the sequence notation “{0..8}”.
[ for i in {0..8} -> (i, true) ]
This “->” symbol can be thought of as a yield operator.
So whenever we want to generate a value, we can use the yield operator (“->”) followed by an expression to generate a value or a set of values.
- We then place this expression within brackets.Therefore the expression, “[ for i in {0..8} -> (i, true) ]”, will generate a list of nine elements.
- When we place data inside brackets then we are really expressing a list of data.
- Now let’s validate that all rows share a common value.
rows |> Seq.forall (fun row ->
row |> Seq.forall (fun kvp -> kvp.Value)) |> should equal true
When programming in F#, we have to remember that by default, there are no objects. Instead, the default consists of two things:
- Structures
- Functions
Based on these fundamentals, we cannot view F# as arbitrary objects relying on their own methods to process data.
Instead we need to swap this paradigm:
Try to view F# as arbitrary functions that operate on values (primitive or collections) to generate results.
Again, do not try to read lines of F# as if you’re reading C# code.
Instead, read F# like a set of stages from left to right where “|>” can be read as a “pipeline”:
Data3 = data1 |> function1() -> data2 |> function2() -> ↓
↑ ← ← ← ← ← ← ← ← ← ← ←← ← ← ←← ← ← ←←