2010-11-03 3 views
3

Retour de l'interview. Je partage avec vous et une bonne réponse précise est la bienvenue.Liste, ne pas perdre la référence

Le but, vous avez une méthode statique, cette méthode reçoit un IList<int> vous avez pour récupérer les valeurs que vous pouvez diviser par 3 et faire le code.

Contrainte: La liste initiale (pour l'essentiel) comporte une référence sur la pile et les valeurs sur le tas, le résultat doit être retour (il est un procédé de vide) dans le même espace (sur le tas) que la liste originale. La solution affichée ici n'est pas correcte car dans la méthode un nouveau pointeur sur la pile + tas est créé dans le domaine de la méthode. Solution ?

Bonus: comment modifier le code pour recevoir non seulement int, mais 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>(); 
    } 

Répondre

8

Ce sens est que le stockage interne à un IList n'est pas sous votre contrôle. L'ajout (ou la suppression éventuelle) d'éléments peut réaffecter les structures de données internes.

Il est particulièrement dénué de sens car la liste contenue dans votre exemple contient la valeur les types qui sont copiés de toute façon lorsque vous y accédez.

Enfin et surtout, il s'agit essentiellement d'utiliser un langage géré pour ne pas avoir à vous soucier des emplacements de mémoire (al). De telles choses sont implementation details de la plate-forme.

Pour répondre à votre question sur les bonus: Il n'y a pas de moyen simple d'y parvenir. On pourrait penser que l'utilisation de génériques avec une contrainte de type résoudrait le problème ici (quelque chose comme static void CanBeDivedByThree<T>(IList<T> list) where T : struct), mais le problème est que C# n'a pas (encore?) De support pour l'arithmétique générique. C# n'a pas d'opérateur modulo qui peut prendre un paramètre générique de type 'T' et 'int'.

+0

Détails ou non cela fait partie des questions de l'entretien, même si je suis d'accord avec vous. –

+2

@ Kris-I: Ces questions sont à mon humble avis mieux répondu en expliquant pourquoi ils n'ont pas de sens. –

+0

@ 0xA3: pour une liste générique, il est raisonnable de filtrer sur un prédicat générique fourni par l'utilisateur. – Vlad

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

ou

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

La première approche ne fonctionne que pour List<T>.

On pourrait en faire une méthode de modèle, mais l'opération restante n'a pas beaucoup de sens sur les flottants.

+0

Votre index de départ est désactivé par un – CodesInChaos

+0

En effet, merci! Déjà corrigé. – Vlad

2

Malheureusement, seule List, mais pas IList, implémente RemoveAll. Donc, je l'implémente d'abord comme une méthode d'extension.

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; 
} 

Ensuite, vous pouvez utiliser:

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

Vous pouvez ensuite utiliser pour d'autres types de surcharges. Malheureusement, vous ne pouvez pas (facilement) le rendre générique puisque les génériques ne fonctionnent pas avec la surcharge de l'opérateur.

+0

Vous pouvez utiliser 'RemoveAt' (supporté par' IList <> ') pour plus de clarté. – Vlad

+0

Typo Je suppose? - vous devriez seulement garder les entrées divisibles par 3, ne pas les supprimer. – BrokenGlass

+1

@Vlad: Ne supprimerait-on pas un algorithme O (n^2)? @BrokenGlass Copier/coller une erreur de la solution de Vlad – CodesInChaos

1

D'autres ont couvert la partie de la liste - ceci est juste pour le bit bonus.

Vous ne pouvez pas le faire de manière statique en utilisant les génériques C#, mais si vous utilisez C# 4, vous pouvez le faire avec la frappe dynamique.Par exemple:

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); 
      } 
     } 
    } 
} 
+0

Le problème principal n'est pas de perdre la référence sur la section de tas d'origine –

+1

@ Kris-I: Comme indiqué ailleurs, il n'y a aucune garantie quant à l'implémentation de 'IList ' sera utilisé. Heck, il pourrait être un paresseusement généré. Si c'était un 'List ' explicite à la place, ce serait différent. –

+0

Nice "hack" avec 'dynamic' :-) Je suis d'accord que ce serait différent avec une liste explicite' '; Cependant, nous savons seulement que le stockage interne d'un 'List ' est un tableau. Nous ne savons pas comment ce stockage est géré et comment et quand le stockage est réaffecté. Et même si nous savions que (par exemple à partir de Reflector) un correctif ou une nouvelle version du framework pourrait le changer. –

Questions connexes