Like most LINQ methods,
Skip
is designed to work with enumerables, not arrays.
public static System.Collections.Generic.IEnumerable<TSource> Skip<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, int count);
It is OK to pass in an array, but it will implicitly be
upcast
to the more generalized type
IEnumerable,
and that is also the type that will be returned by Skip
.
int[] whatGoesIn = {1, 2, 3};
IEnumerable<int> whatComesOut = whatGoesIn.Skip(1);
You cannot safely
downcast
the return value to int[]
.
As proposed by others, LINQ has a convenient method
ToArray,
but think twice before using it.
The method will create a completely new array object, which is often a total waste of memory as well as CPU time.
Only use this method
when you really need an array.
In your case, ToArray
would have to be called once for every element of the original array.
This will put a lot of strain on the heap.
With a big array, you will be seeing a lot of GC.
Make up your mind: either use Skip
, or use arrays. Don't do both.
Option 1: Skip, without arrays
Follow the LINQ philosophy and use enumerables all the way.
This has the advantage that it will work on any enumerable: arrays, collections, lists, dictionaries...
So instead of declaring parameter a
as an array:
public static bool Aa(int[] a, int k)
declare a
as an enumerable:
public static bool Aa(IEnumerable<int> a, int k)
Notice this will immediately eliminate your error. It will introduce a few new ones though. Like in the for
loop; IEnumerable<int>
has no property Length
.
for (int i = 1; i < a.Length; i++)
if (a[0] + a[i] == k)
return true;
Upon close inspection, you are basically looking for an array element that equals k - a[0]
. Just use
Contains:
bool found = a.Skip(1).Contains(k - a.First());
if (found) return true;
Another reference to Length
:
if (a.Length != 1)
Aa(a.Skip(1), k);
return false;
That's weird; you call Aa
but don't do anything with its return value. You probably meant this:
if (a.Length != 1)
return Aa(a.Skip(1), k);
return false;
I will not use Count,
as it is potentially expensive on long enumerables.
I am not actually interested in the exact length; we can stop counting after the second element.
return a.Skip(1).Any() && Aa(a.Skip(1), k);
After refactoring, the whole function becomes a one-liner:
public static bool Aa(IEnumerable<int> a, int k)
{
return a.Skip(1).Contains(k - a.First()) || (a.Skip(1).Any() && Aa(a.Skip(1), k));
}
I'd recommend to make the function robust against zero-length arrays by moving the 'Any' check to the front.
public static bool Aa(IEnumerable<int> a, int k)
{
return a.Any() && (a.Skip(1).Contains(k - a.First()) || Aa(a.Skip(1), k));
}
Option 2: arrays, without Skip
Arrays have one big advantage over enumerables: they are fast.
OP's for
loop is faster than my call to Contains
.
There is no need to use Skip
; just start iterating at a different index.
public static bool Aa(int[] a, int k, int start = 0)
{
for (int i = start + 1; i < a.Length; i++)
if (a[start] + a[i] == k)
return true;
if (start < a.Length)
return Aa(a, k, start + 1);
return false;
}
This is twice as fast as the ToArray
solution, and three times as fast as enumerables.
Note
I'm not too thrilled about the use of recursion here,
because nesting depth is proportional to array length.
For long arrays, this may cause a stack overflow.
In theory, the compiler could optimize away the tail recursion, but even in .NET 6, it doesn't
(although this answer suggests otherwise).