Reputation: 6744
I am trying to implement a function stride n a
where n
is a stride length and a
is an array. Given a call like stride 2 [| "a"; "b"; "c"; "d" |]
it should return something like [ [|"a"; "b"|]; [|"c"; "d" |] ]
. I am brand new to F# and don't know anything about using arrays in a functional language. I know what I've written is garbage, but it's a start:
let stride n (a : array 'a) =
let rec r ind =
if ind >= a.length()
then
[]
else
a[ind .. (ind + n - 1)]::r(ind + n)
in
r 0
(see also on dotnetfiddle). This does not compile. I to added the array 'a
type parameter because the compiler couldn't find the length
method, but this type parameter does not appear to be allowed.
For context, I am trying to get groups of letters from a string, so I plan to call this like stride 2 myString.ToCharArray()
.
Upvotes: 1
Views: 109
Reputation: 80805
First of all, there is already an app for that - it's called Array.chunkBySize
:
> Array.chunkBySize 2 [|1;2;3;4;5;6;7|]
val it : int [] [] = [|[|1; 2|]; [|3; 4|]; [|5; 6|]; [|7|]|]
And there are similar functions in the Seq
and List
module, so if your goal is to work with strings, I would consider using the Seq
variant, since string
already implements the seq
interface:
> Seq.chunkBySize 2 "abcdefg";;
val it : seq<char []> =
seq [[|'a'; 'b'|]; [|'c'; 'd'|]; [|'e'; 'f'|]; [|'g'|]]
But if you're interested in education rather than GSD, then here it is:
The logic of the code is fine, except you have a few purely syntactic mistakes and one logical.
First, the type "array of a" is not denoted array 'a
. In general the F# notation for generic types is either T<'a>
or 'a T
, for example list<int>
or int list
. However, this doesn't work for arrays, because arrays are special. There is a type System.Array
, but it's not generic, so you can't use it like that. Instead, the idea of an array is kind of baked into the CLR, so you have to use the special syntax, which is 'a[]
.
So: a : 'a[]
instead of a : array 'a
Second while array does have a length property, it's capitalized (i.e. Length
) and it's a property, not a method, so there shouldn't be parens after it.
So: a.Length
instead of a.length()
However, that is not quite the F# way. Methods and properties are meh, functions are way better. The F# way would be to use the Array.length
function.
So: Array.length a
instead of a.length()
Bonus: if you do that, there is no need for the type annotation : 'a[]
, because the compiler can now figure it out from the type of Array.length
.
Third, indexing of arrays, lists, and everything else that has an index, needs a dot before the opening bracket.
So: a.[ind .. (ind + n - 1)]
instead of a[ind .. (ind + n - 1)]
Fourth, the in
is not necessary in F#. You can just omit it.
With the above modifications, your program will work.
But only on arrays whose length is a multiple of n
. On all others you'll get an IndexOutOfRangeException
. This is because you also have...
The logical mistake is that while you're checking that ind
is within the array bounds, you're not checking that ind + n - 1
is as well. So you need a third case in your branch:
if ind >= Array.length a then
[]
elif ind + n - 1 >= Array.length a then
a.[ind..] :: r (ind+n)
else
a.[ind .. (ind+n-1)] :: r (ind+n)
Now this is ready for prime time.
Upvotes: 3