2010-03-09 7 views
6

Le code ci-dessous est conçu pour insérer une chaîne et supprimer un ensemble de mots arbitraires considérés comme non essentiels à une phrase de recherche..NET Amélioration des performances d'analyse de chaîne - Odeur de code possible

Je n'ai pas écrit le code, mais j'ai besoin de l'incorporer dans quelque chose d'autre. Ça marche, et c'est bien, mais ça me fait mal. Cependant, je n'arrive pas à sortir la tête de la boîte que cette méthode a créée pour penser à une autre approche. Peut-être que je ne fais que le rendre plus compliqué que nécessaire, mais je pense que cela pourrait être plus propre avec une technique différente, peut-être en utilisant LINQ.

Toutes les suggestions sont les bienvenues. y compris la suggestion que je suis trop penser et que le code existant est parfaitement clair, concis et performant.

Alors, voici le code:

private string RemoveNonEssentialWords(string phrase) 
{ 
    //This array is being created manually for demo purposes. In production code it's passed in from elsewhere. 
    string[] nonessentials = {"left", "right", "acute", "chronic", "excessive", "extensive", 
            "upper", "lower", "complete", "partial", "subacute", "severe", 
            "moderate", "total", "small", "large", "minor", "multiple", "early", 
            "major", "bilateral", "progressive"}; 
    int index = -1; 

    for (int i = 0; i < nonessentials.Length; i++) 
    { 
     index = phrase.ToLower().IndexOf(nonessentials[i]); 
     while (index >= 0) 
     { 
      phrase = phrase.Remove(index, nonessentials[i].Length); 
      phrase = phrase.Trim().Replace(" ", " "); 
      index = phrase.IndexOf(nonessentials[i]); 
     } 
    } 

    return phrase; 
} 

Merci d'avance pour votre aide.

Cheers,

Steve

Répondre

11

Cela semble être un algorithme pour supprimer les mots d'arrêt d'une phrase de recherche.

Voici une pensée: Si cela est en fait utilisé pour une recherche, avez-vous besoin de la phrase résultante pour être une représentation parfaite de l'original (avec tous les espaces d'origine intacts), mais avec des mots d'arrêt supprimés, ou peut-il être «assez proche» pour que les résultats soient toujours les mêmes? Une approche serait de marquer la phrase (en utilisant l'approche de votre choix - pourrait être une regex, je vais utiliser une simple division), puis de le réassembler avec les mots d'arrêt supprimés. Exemple:

public static string RemoveStopWords(string phrase, IEnumerable<string> stop) 
{ 
    var tokens = Tokenize(phrase); 
    var filteredTokens = tokens.Where(s => !stop.Contains(s)); 
    return string.Join(" ", filteredTokens.ToArray()); 
} 

public static IEnumerable<string> Tokenize(string phrase) 
{ 
    return string.Split(phrase, ' '); 
    // Or use a regex, such as: 
    // return Regex.Split(phrase, @"\W+"); 
} 

Cela ne vous donnera pas exactement le même résultat, mais je vais parier qu'il est assez proche et il va certainement courir beaucoup plus efficacement. Les moteurs de recherche réels utilisent une approche similaire à celle-ci, puisque tout est indexé et recherché au niveau du mot, pas au niveau du personnage.

+0

J'aime aussi séparer l'entrée en mots séparés. Sera également utile pour appliquer la logique future sur chaque terme de recherche. par exemple. Vérification de l'orthographe –

+1

Si la performance doit être maximisée, il convient de noter que cette méthode contient encore beaucoup d'inefficacité. Tokenizing la chaîne d'entrée créera, évidemment, autant de chaînes séparées qu'il y a de mots dans la chaîne d'entrée. De même, la création du tableau et la reconnexion des mots peuvent prendre un certain temps si l'entrée est grande. –

+2

@qstarin: Considérant qu'une phrase de recherche est peu susceptible d'être plus de, oh, environ 10 mots, je doute que cela va poser un problème important.Passez un 'HashSet ' pour l'argument 'stop' et cela devient O (N) sur le nombre de mots; s'inquiéter de la performance au-delà de ce point devient optimisation IMO prématurée. Viser un code propre et lisible qui fonctionne * raisonnablement * bien d'abord; alors, si ce n'est pas suffisant, vous pouvez commencer à faire des micro-optimisations. – Aaronaught

3

je voudrais utiliser une expression régulière (créée à l'intérieur de la fonction) pour cette tâche. Je pense qu'il serait capable de faire tout le traitement à la fois sans avoir à faire de multiples passages à travers la chaîne ou avoir à créer plusieurs chaînes intermédiaires.

private string RemoveNonEssentialWords(string phrase) 
{ 
    return Regex.Replace(phrase, // input 
         @"\b(" + String.Join("|", nonessentials) + @")\b", // pattern 
         "", // replacement 
         RegexOptions.IgnoreCase) 
      .Replace(" ", " "); 
} 

Le \b au début et à la fin de la configuration fait en sorte que le jeu est sur une limite entre les caractères alphanumériques et les caractères non alphanumériques. En d'autres termes, il ne correspondra pas seulement à une partie du mot, comme le fait votre exemple de code.

+0

Bien que vous deviez construire dynamiquement l'expression régulière basée sur la liste de mots, puisque c'est un paramètre de la fonction dans la version de production, et non un tableau constant. –

1

Oui, ça sent. J'aime les petites machines à états pour l'analyse, elles peuvent être autonomes dans une méthode utilisant des listes de délégués, boucler les caractères dans l'entrée et envoyer chacun à travers les fonctions d'état (dont j'ai retourné la fonction d'état suivante basé sur le caractère examiné).

Pour des performances, je débusquer des mots entiers à un constructeur de chaîne après que je l'ai frappé un caractère de séparation et vérifié le mot contre la liste (peut utiliser un ensemble de hachage pour cela)

5

Je suppose que votre code n'est pas faire ce que vous voulez faire de toute façon. "modéré" serait converti en "d" si j'ai raison. Pour obtenir une bonne solution, vous devez spécifier vos besoins un peu plus en détail. J'utiliserais probablement des expressions Remplacer ou régulières.

+0

J'allais simplement le signaler, mais en utilisant 'lefty' à 'y'. – Mark

+0

J'ai aussi relevé le défaut du code. Plutôt que de travailler sur la phrase en une seule chaîne, il faudra la manipuler comme un ensemble de mots. –

+0

De même si "lefacutet" apparaît dans la phrase, il supprimera "acute" et laissera "left", même si "left" est un non essentiel. – Brian

1

Je voudrais créer une table de hachage de mots supprimés analyser chaque mot si dans le hachage l'enlever une seule fois à travers le tableau et je crois que la création d'une table a est O (n).

0

Comment cela ressemble-t-il?

 foreach (string nonEssent in nonessentials) 
     { 
      phrase.Replace(nonEssent, String.Empty); 
     } 
     phrase.Replace(" ", " "); 
+2

Cela fonctionne à peu près le même que le code d'origine, et souffre encore de tous les problèmes que d'autres affiches ont souligné avec le code original, mais il est plus propre et plus facile à lire. Un analyseur/machine à états qui divise l'entrée en mots pourrait être globalement meilleur. –

0

Si vous voulez aller sur la route Regex, vous pouvez le faire comme ça. Si vous voulez de la vitesse, cela vaut la peine d'essayer et vous pouvez comparer/contraster avec d'autres méthodes:

Commencez par créer une Regex à partir de l'entrée du tableau. Quelque chose comme:

var regexString = "\\b(" + string.Join("|", nonessentials) + ")\\b"; 

Cela se traduira par quelque chose comme:

\ b (left | right | chronique) \ b

Ensuite, créez un objet Regex pour faire la recherche/remplacer:

System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(regexString, System.Text.RegularExpressions.RegexOptions.IgnoreCase); 

Ensuite, vous pouvez juste faire remplacer comme ceci:

string fixedPhrase = regex.Replace(phrase, ""); 
Questions connexes