4

Back from interview. I share with you and a good and precise answer is welcome.

The purpose, you have a static method, this method receive an IList<int> you have to get back the values you can divise by 3 and make the code.

Constraint : The original list (in the main) has a reference on the stack and the values on the heap, the result must be return (it's a void method) in the same space (on the heap) than the original list. The solution show here is not correct because in the method a new pointer on the stack + heap are created in the method domain. Solution ?

Bonus : how change the code to receive not only int but float, double, ....

static void Main(string[] args)
    {
        IList<int> list = new List<int>() { 9, 3, 10, 6, 14, 16, 20};
        CanBeDivedByThree(list);
    }

    static void CanBeDivedByThree(IList<int> list)
    {
        list = (from p in list
                where p % 3 == 0
                orderby p descending
                select p).ToList<int>();
    }
TheBoubou
  • 19,487
  • 54
  • 148
  • 236

4 Answers4

9

That's meaningless as the internal storage to an IList is not under your control. Adding (or possibly removing) items might re-allocate the internal data structures.

It is especially meaningless as the list in your sample contains value types which are copied anyway when you access them.

Last but not least it's basically the whole point of using a managed language that you don't have to worry about memory (al)locations. Such things are implementation details of the platform.

To take up on your bonus question: There is no simple way to achieve that. One could think that using generics with a type constraint would solve the problem here (something like static void CanBeDivedByThree<T>(IList<T> list) where T : struct), but the problem is that C# does not (yet?) have support for generic arithmetic. C# doesn't have a modulo operator that can take a generic parameter of type 'T' and 'int'.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
  • Details or not it's part of the interview questions even if I agree with you. – TheBoubou Nov 03 '10 at 12:05
  • 2
    @Kris-I: Such questions are IMHO best answered by explaining why they don't make sense. – Dirk Vollmar Nov 03 '10 at 12:07
  • @0xA3: for a generic list it's reasonable to filter on a generic user-supplied predicate. – Vlad Nov 03 '10 at 12:19
  • @0xA3: I meant something like the @CodeInChaos's approach: `void RemoveAll(IList list, Predicate match)`. – Vlad Nov 03 '10 at 12:32
  • I agree with the arguments. Even better: normaly there are more than one correct answers to a question. It only matters on your arguments... – Yves M. Nov 03 '10 at 13:01
  • 1
    I think it's just a badly written requirement. I think what was meant is that the result ends up in the instance of IList that was passed in, and not in a new instance. – CodesInChaos Nov 03 '10 at 13:30
  • 1
    Two options: either intentional trick question to see if you understand the concept of .NET as managed language, or they hired someone without much experience to write the questions. :) – Shadow The GPT Wizard Nov 03 '10 at 14:13
2
list.RemoveAll(n => n % 3 == 0);

or

for (int i = list.Count - 1; i >= 0; --i)
{
    if (list[i] % 3 != 0)
        list.RemoveAt(i);
}

The first approach works only for List<T>.

One could make it a template method, but remainder operation doesn't make much sense on floats.

Vlad
  • 35,022
  • 6
  • 77
  • 199
2

Unfortunately only List but not IList does implement RemoveAll. So I first implement it as an extension method.

public static int RemoveAll<T>(this IList<T> list, Predicate<T> match)
{
  if (match == null)
    throw new ArgumentNullException("match");

  int destIndex=0;
  int srcIndex;
  for(srcIndex=0;srcIndex<list.Count;srcIndex++)
  {
    if(!match(list[srcIndex]))
    {
      //if(srcIndex!=destIndex)//Small optimization, can be left out
        list[destIndex]=list[srcIndex];
      destIndex++;
    }
  }
  for(int removeIndex=list.Count-1;removeIndex>=destIndex;removeIndex--)
  {
    list.RemoveAt(removeIndex);
  }
  return srcIndex-destIndex;
}

Then you can use:

list.RemoveAll(n => n % 3 != 0);

You can then use overloads for other types. Unfortunately you can't (easily) make it generic since generics don't work with operator overloading.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • You could use `RemoveAt` (supported by `IList<>`) for clarity. – Vlad Nov 03 '10 at 12:18
  • Typo I assume? - you should actually only keep the entries dividable by 3, not remove them. – BrokenGlass Nov 03 '10 at 12:40
  • 1
    @Vlad: Wouldn't RemoveAt result in a O(n^2) algorithm? @BrokenGlass Copy/Paste mistake from Vlad's solution – CodesInChaos Nov 03 '10 at 13:24
  • @CodeInChaos: it will. In this task here I prefer clarity over efficiency (remembering about the premature optimizations and letting the optimizer do the job). – Vlad Nov 03 '10 at 13:49
  • @CodeInChaos: if you want to optimize even more, maybe the following would do: you can split the whole into 2 loops: loop #1 skips until found first non-matching item (no need to compare src/dest indices), and the loop #2 (which starts where the loop #1 finished) always copies (because src/dest indices are guaranteed not equal). – Vlad Nov 03 '10 at 13:51
  • O(n) vs O(n^2) is quite relevant even for relatively small lists. And I view this as a general purpose library function. The check for equal indices is probably premature optimization. – CodesInChaos Nov 03 '10 at 13:58
1

Others have covered the list part - this is just for the bonus bit.

You can't do this in a statically typed way using C# generics, but if you're using C# 4 you can do it with dynamic typing. For example:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        ShowDivisibleBy3(new List<int> { 1, 3, 6, 7, 9 });
        ShowDivisibleBy3(new List<decimal> { 1.5m, 3.3m, 6.0m, 7m, 9.00m });
    }

    static void ShowDivisibleBy3<T>(IEnumerable<T> source)
    {
        foreach (dynamic item in source)
        {
            if (item % 3 == 0)
            {
                Console.WriteLine(item);
            }
        }
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • The main problem is not lose the reference on the original heap section – TheBoubou Nov 03 '10 at 12:36
  • 1
    @Kris-I: As expressed elsewhere, there's no guarantee about what implementation of `IList` will be used. Heck, it could be a lazily generated one. If it were an explicit `List` instead, that would be different. – Jon Skeet Nov 03 '10 at 12:41
  • Nice "hack" with `dynamic` :-) I agree that it would be different with an explicit `List`; however we only know that the internal storage of a `List` is an array. We don't know how this storage is managed and how and when the storage gets re-allocated. And even if we knew that (e.g. from Reflector) a patch or new framework version might change it. – Dirk Vollmar Nov 03 '10 at 13:57
  • @0xA3: Exactly, hence my comment. – Jon Skeet Nov 03 '10 at 14:22