F# combine 2D array function for general data type

Category: visual studio fsharp

Question

zydjohn on Wed, 14 Jun 2017 19:14:56


Hello:

I need a function to do the following:

For example:

let a0 = [| for i in 1..100 do yield i |]

let aFin = cmbTrunc(a0, 10, 4, 20)

The function cmbTrunc(series: ‘a[], offset: int, column: m, size: s)

Can do this:

It will create a 2D array with m number of columns, the data in the first column is the from index of offset in the original array series; the total number of elements taken for the first column is size s; the data in the second column is the from index of offset + 1 in the original array series; the total number of elements taken for the first column is size s;

For this example: let a0 = [| for i in 1..100 do yield i |]

let aFin = cmbTrunc(a0, 10, 4, 20)

The result is a 2D array with 4-column and each column has 20 elements:

The first column is: [ 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30 ]

The second column is: [ 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31  ]

The third column is: [ 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32 ]

The last column is: [ 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33 ]

I think I can use the following function, but the issue is: it is not easy to do Array.zip if more than 3 arrays are there.

let inline diff (series: 'a[], n: int) =

    if (n < series.Length) then

       Array.mapi (fun i x -> x - series.[i])  series.[n..]

    else Array.empty<'a>

Also, I need the function to handle different data type, since both INT and Float types will have to use the same function.

Thanks,


Replies

Mr. Tines on Wed, 14 Jun 2017 21:15:10


Given the requirement

It will create a 2D array with m number of columns, the data in the first column is the from index of offset in the original array series; the total number of elements taken for the first column is size s; the data in the second column is the from index of offset + 1 in the original array series; the total number of elements taken for the first column is size s;

you've almost written the required code; you just need to go and look at the appropriate collections APIs provided by the language.  You're not doing any arithmetic here, just copying elements, so the `inline` hack won't be required; simple generics should suffice.

Most general solution (all sequences, all the time)

From your input sequence, you are only going to use the elements described by skipping offset entries and taking (s + number of columns - 1) entries.  At that point you may wish to reify the sub-sequence as a replayable buffered sequence so that the number of elements can be checked to be sufficient before proceeding.

The simplest way to proceed would then to note that columns of the array will correspond that sub-sequence windowed with window length s  Matching up column and windowed sequence, and then rows within each column is going to be best done by using the `iteri` method at each level. 

Specialisation 1 -- intermediate array

With the sub-sequence reified as an array (check its length), simply use a couple of indexing integer sequences to populate element .[i, j] of the output with element .[i + j] of the intermediate sub-sequence.

Specialisation 2 -  input is an array

If the input is itself an array, check its length, then forget extracting the sub-sequence and just populate element .[i, j] of the output directly with element .[offset + i + j] from the input.

I see no point at which a zip operation or the `diff` function quoted from one of your previous exercises would be relevant to the task at hand.



 

zydjohn on Thu, 15 Jun 2017 09:30:33


Sorry, I made a mistake:
I want to create a function to do this: 
It will create a 2D array with m number of columns, the data in the first column is the from index of offset in the original array series; the total number of elements taken for the first column is size s; the data in the second column is the from index of offset - 1 in the original array series; the total number of elements taken for the first column is size s;

For this example: let a0 = [| for i in 1..100 do yield i |]
let aFin = cmbTrunc(a0, 10, 4, 20)
The result is a 2D array with 4-column and each column has 20 elements:
The first column is: [ 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30 ]
The second column is: [ 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29 ]
The third column is: [ 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28 ]
The last column is: [ 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27 ]

I think I can understand your suggestion and the original question seems not difficult, but my new or correct question, for me it is not so easy.
If you can share your code, it will be great!
Thanks,

Mr. Tines on Fri, 16 Jun 2017 22:35:10


The change in the specification barely changes the nature of the solution; it just decrements the column start point rather than incrementing it.

In this case, the input array must have at least offset + rows elements, the number of columns must be no more than the offset, and the value of element .[i, j] of the resulting array is now the value at .[offset + i  - j] in the input -- obviously, as .[0,0] is the value at .[offset], stepping one row advances the source index by 1 but stepping one column the reverse.

In the all sequence case,  skip only (offset - columns + 1) entries, rather than the whole of offset, take the same number of entries, and reverse the windowed sequence.  Or take the same number of entries, turn into an array, and treat as the input array case with an offset of columns - 1.