11

Slicing improvements in F# 5

 3 years ago
source link: https://www.compositional-it.com/news-blog/slicing-in-f-and-how-its-a-bit-better-in-f-5?utm_content=buffer8815a&utm_campaign=buffer
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Array slicing is already a very rich feature in F#, and it's been slightly improved in F# 5. In this post we're going to look at slicing in some detail. If you only want to know what's new for F# 5, skip straight to the end.

Sushi slices

What is array slicing?

Array slicing is an F# feature stolen from many programming languages before it. It's a way to get a subset of items from an array.

// Given the array [| 0; 1; 2; ...; 99 |]
let array = [| 0 .. 99 |]

// Get me the items from index 5 to 10 inclusive
array.[5 .. 10] // [|5; 6; 7; 8; 9; 10|]

// From index 10 to 10
array.[10 .. 10] // [|10|]

// If the slice start is later than the slice end then you get an empty array.
array.[10 .. 9] // [||]

You can also slice F# lists and strings in the same way.

[ 0 .. 9 ].[6 .. 8] // [6; 7; 8]

let text = "Time flies like an arrow; fruit flies like a banana"
text.[16 .. 23] // "an arrow"

Unbounded slices

You can omit the boundary at the beginning or end of any slice. This will extend the slice to start or the end of the collection.

let array = [| 0 .. 99 |]

array.[97 ..] // [|97; 98; 99|]

Custom slicing

If you define your own collection type for any reason in F#, you can implement the GetSlice member to allow slicing directly on your type.

type MyCoolCollection<'a> =
    | MyCoolCollection of 'a array
    member this.GetSlice(start, finish) =
        match this with
        | MyCoolCollection arr ->
            let start = start |> Option.defaultValue 0
            let finish = finish |> Option.defaultValue (arr.Length - 1)
            MyCoolCollection arr.[start .. finish]

let collection = MyCoolCollection [| 0 .. 9 |]

collection.[1 .. 2] // MyCoolCollection [|1; 2|]
collection.[8 .. ] // MyCoolCollection [|8; 9|]

Multi-dimensional slicing

If you use multi-dimensional arrays, you can also use slices in each dimension.

let grid =
    [| [| 1; 2; 3 |]
       [| 4; 5; 6 |]
       [| 7; 8; 9 |] |]
    |> array2D

// Normal index access of individual items.
// On the first row, get the second item.
grid.[0, 1]
// 2

// Using a slice on one dimension.
// On the first row, get the first two items.
grid.[0, 0 .. 1]
// int [] = [|1; 2|]

// Using slices in both dimensions.
// Get items that are in the first two rows and first two columns.
grid.[0 .. 1, 0 .. 1]
// [[1; 2]
//  [4; 5]]

// Using a partially unbounded slice in one dimension.
// Get items that are in the first two rows and skip the first column.
grid.[0 .. 1, 1 ..]
// [[2; 3]
//  [5; 6]]

// Using a completely unbounded in one dimension.
// Get the first row, for all columns.
grid.[0, *]
// [|1; 2; 3|]

// Get the first column for all rows.
grid.[*, 0]
// [|1; 4; 7|]

Note that whenever a fixed index is accessed for only one of the two dimensions, the result will be an array instead of a 2D array.

How is slicing better in F# 5?

Consistency and runtime safety: In F# 5, slicing is slightly more consistent between lists, arrays and strings in a way that is more permissive. For all collection types, if you specify a range that is out of range, you will now get an empty collection instead of a runtime exception. This is useful considering the slice range is often calculated rather than written as a numeric literal, as in my examples. We can now be confident our code won't throw exceptions and the empty collection is usually a more useful result.

[0 .. 3].[5 .. 6] // []
[|0 .. 3|].[5 .. 6] // [||]
"0123".[5 .. 6] // ""

Mixing fixed index and slices for 3D and 4D arrays: Before F# 5, you couldn't use a mix of fixed indexes and slices between the different dimensions of 3D and 4D arrays (this is something that I have already demonstrated for 2D arrays above). But now you can.

Warning: This is still marked as an experimental feature for F# 5 and requires you to be using the preview version.

let cube = Array3D.init 2 2 2 (fun i j k -> (i, j, k))
cube.[0, *, 0 .. 1]
// [[(0, 0, 0); (0, 0, 1)]
//  [(0, 1, 0); (0, 1, 1)]]

Happy slicing! 🍕🔪🍰🔪🥧🔪🍣


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK