Strange array slice behavior

> let a = [| 'a'..'d' |];; val a : char [] = [|'a'; 'b'; 'c'; 'd'|] 

Make a trivial cut:

 > a.[1..2], [1..2];; val it : char [] * int list = ([|'b'; 'c'|], [1; 2]) 

Now try with an empty area:

 > a.[1..0], [1..0];; val it : char [] * int list = ([||], []) 

It seems working and reasonable - we got two empty sequences.

But this does not work here:

 > a.[5..0];; System.OverflowException: Arithmetic operation resulted in an overflow. at <StartupCode$FSI_0018> .$FSI_0018.main@ () Stopped due to error 

Of course, there is a workaround [| for i in [5..0] -> a.[i] |] [| for i in [5..0] -> a.[i] |] . But I do not understand why a.[5..0] fails? Why not just return an empty array? Any reasons for this behavior?

+6
source share
1 answer

This is mistake.

Although the definition of an array and the expression of a range have different concepts (you cannot use a.[1..2..5] , for example), they should behave sequentially.

Note that the exception occurs with a.[start..finish] when finish - start <= -2 ( a.[3..1] does not work), and an array slice works fine if finish - start = -1 ( a.[5..4] = [||] ).

Array slicing is done using GetArraySlice in prim-types.fs :

 let inline GetArraySlice (arr: _[]) start finish = let start = (match start with None -> 0 | Some n -> n) let finish = (match finish with None -> arr.Length - 1 | Some n -> n) GetArraySub arr start (finish - start + 1) 

whereas GetArraySub is implemented in the same module as follows:

 let inline GetArraySub arr (start:int) (len:int) = let dst = zeroCreate len for i = 0 to len - 1 do SetArray dst i (GetArray arr (start + i)) dst 

If finish - start = -1 , we have len = 0 in GetArraySub , and zeroCreate 0 returns an empty array. This is no longer the case with finish - start <= -2 , which results in len < 0 and zeroCreate len fails.

This can be fixed by always returning an empty array whenever finish - start <= -1 .

+5
source

Source: https://habr.com/ru/post/913847/


All Articles