2017-05-09 3 views
0

Ich habe das Gefühl, dass, was ich suche, sollte existieren, aber ich weiß nicht, wie es heißt. Alle Suchen nach "regex for objects" geben nur Tutorials und Fragen zur normalen RegEx zurück. Die Suche nach "pattern matching" bringt Neuigkeiten über die neue Musterübereinstimmungsfunktion von C# 7, was ich nicht erreichen möchte.Gibt es etwas wie RegEx, aber für strukturierte Objekte?

Um zu zeigen, was ich nach, übernehmen Sie die folgende Klasse haben:

public class Car 
{ 
    public string Color { get; set; } 
    public int MilesDriven { get; set; } 
    public bool IsAllWheelDrive { get; set; } 
} 

Und dann annehmen, dass Sie eine Liste der Auto-Objekte mit zufälligen und vielfältigen Eigenschaften aufweisen. Ich würde gerne in der Lage sein, die Liste nach RegEx-ähnlichen Mustern zu durchsuchen und die Anfangs- und Endindizes jeder Instanz des Musters zu erhalten.

Beispiel Muster wären:

Suche alle Fälle, in denen ein weißes Auto mit Allradantrieb zwischen zwei blauen Autos auftritt und das erste blaue Auto hat mehr als 1000 Meilen auf dem Tacho.

Finden Sie alle Fälle, in denen einem roten Auto sofort mindestens 2 grüne Autos folgen und schließlich ein Auto mit weniger als 100 Meilen darauf folgt.

Dies ist ein bisschen eine konstruierte Frage, aber ich würde gerne wissen, ob so etwas existiert, vorzugsweise als eine vorhandene C# -Bibliothek.

Entschuldigung, wenn "pattern-matching" kein anwendbares Tag für diese Frage ist, aber wie gesagt, ich weiß nicht, was, wenn überhaupt, das nennen soll.

+1

Ich bin nicht bekannt, dass Bibliotheken, die dies tun, aber Sie könnten vielleicht eine 'toString()' Methode für das Objekt definieren und stattdessen Regex darauf ausführen. Ich versuche mir das als eine Linq-Abfrage vorzustellen (vorausgesetzt, Sie hatten irgendeine Art von Ordnung in das Objekt eingebaut) und ich muss mir vorstellen, dass das auch wirklich hässlich aussieht. –

+0

@EdPlunkett nicht einmal in der Nähe eines Duplikats von diesem. Diese Frage fragt *, * worüber sie tatsächlich sprechen, es gibt Leute, die nicht einmal wissen, dass LINQ existiert, und dies deshalb als Duplikat zu markieren, wäre kontraproduktiv. – snb

+0

@dasblinkenlight Ich habe mich sehr stark auf LINQ verlassen, um teilweise nach sehr spezifischen Mustern zu suchen, aber es ist nichts, was ich als die Lösung betrachte, nach der ich suche (es sei denn, ich habe etwas offensichtlich übersehen). Können Sie einen LINQ-Ausdruck schreiben, der eines der von mir bereitgestellten Beispielmuster löst? – stevcode

Antwort

0

Was Sie suchen, ist das Filter Konzept. C# bietet eine Anwendung eines solchen Konzepts für Listen, es wird jedoch kompliziert, wenn Sie nicht nur versuchen, die Attribute des Objekts zu filtern. Microsoft macht die filitering Fähigkeit generic accross C# mit LINQthis post See, oder this post für ein Beispiel:

Filtering collections in C#

Grundsätzlich ist die Syntax:

var newlist = list.linqquery1.linquery2...linqueryN.Where(s.x condition); 

natürlich, wenn seine einfach genug, um Sie das tun können, folgende:

var newlist = list.Where(s.x condition); 

Aber Ihr Problem fordert auch selec basierend auf nachfolgenden Punkten in der Liste. Das ist sehr viel komplizierter, weil Sie nicht auf diese Elemente zugreifen können, wenn Sie diese Daten nicht an das Element in der Liste anhängen. Wenn Ihr listelem beispielsweise ein doubly linked list-Knoten ist, könnten Sie in der Liste für Elemente nach Station nur auf einer Vorwärtsknoten-Referenz basieren und eine Bedingung wie diese ausführen (um zu prüfen, ob zwei grüne Autos folgen):

var green2follows = carlist.Where(s.next.type == greencar && s.next.next.type == greencar); 

Sie könnten jedoch von einer Situation ausgehen, in der es nicht notwendig wäre, doppelt verkettete Listen zu verwenden, wenn Sie dies selbst mit Iteration iteriert haben. Da LINQ hauptsächlich auf aufzählungbasierten Abfragen arbeitet, müssen Sie leider eine Umgebung finden, in der Microsofts integrierte Dienstprogramme zum Filtern verwendet werden (obwohl dies nicht für Microsoft typisch ist, in der Regel enthalten Sie keinen Ort in Abfragen). This post deckt diese Schlussfolgerung ab.

Um dies iterativ zu tun, würden Sie eine for-Schleife erstellen und gegen i + 1 und i + 2 Werte von Autos testen. Seien Sie vorsichtig, da dies mit früheren Werten unordentlich wird (i - n) Iteratoren könnten dafür gut sein, um Fehler zu vermeiden, obwohl ich nicht sicher bin, ob C# Iteratorarithmetik wie andere Sprachen unterstützt, damit Sie rückwärts und vorwärts gehen können. Möglicherweise müssen Sie einen benutzerdefinierten Iterator erstellen, um diese Art von Filter generisch zu definieren.

EDIT: können Sie vermeiden, eine benutzerdefinierte Iterator erstellen und lediglich erstellen benutzerdefinierte Objekt vom Iterator zurückgegeben, die vorwärts und rückwärts schaut über Iterator Blöcke (wie die answer suggest in this post)

unterstützt Was Sie vielleicht ist so etwas wie dies:

HandleObject<T>... 
... 
public bool backwardsWhere(condition)... 
public bool forwardsWhere(condition)... 
public bool backwardsNWhere(n, condition); 
public bool forwardsNWhere(condition); 
//other forwards filter functions for convienience 

// get back the element that we wraped 
public T get(); 



static IEnumerable<T> iteratorBlock(List<T> list) 
{ 
    foreach (int i = 0; i < list.Length; i++) 
    { 
     // yield means a new HandleObject is only created upon access, and 
     // need not be stored otherwise 
     yield return HandleObject<T>(i, list); 
    } 
} 

statt Ihre Liste und Linqing gegenüber der Verwendung, benutzen Sie diesen Iterator stattdessen auf Ihrer Liste als Templat, auf diese Weise können Sie Ihre eigenen nicht mit fieser Iterator Semantik in C# zu tun haben für die Erstellung von whilist noch sein in der Lage, al l Die Funktionalität, die Sie benötigen, um über die Liste zu gehen.

var customIterableFromBlock = iteratorBlock<Car>(carlist); 
var selectedCars = from handleCar in customIterableFromBlock 
where ... //handleCar.backwardsWhere(...)&& handleCar.forwardsWhere(...) 
      //handleCar.get().x == condition etc 
select Car 
{ 
    //get Car from handleCar.get() 
}; 

Der Grund, warum Sie i und die Liste selbst in den HandleObject Konstruktor umfassen würde, ist für Vorwärts- und Rückwärtssuche durch die Liste basierend auf der Position zu ermöglichen i eingeleitet.

+0

Ja, der Prozess, den du in deinem letzten Absatz gegeben hast, ist die genaue Methode, die ich bisher benutzt habe; hartes Kodieren einer Suche nach einem spezifischen Muster pro Iterationsschleife. Ich hatte gehofft, dass etwas einfacher existierte, bevor ich versuchte, einen benutzerdefinierten Iterator zu erstellen, der generisch jede Art von Suche behandelt. – stevcode

+0

@stevcode Sie müssen keine separaten Muster pro Iterationsschleife haben, sondern nur die Muster in der gleichen Schleife erstellen. 'for (...) {if condition1 (Index, Liste) && condition2 (Index, Liste) ... etc}' Sie können die Bedingungen in der gleichen Schleife – snb

+0

@stevcode Zusätzlich trotz, was Sie beschreiben, tatsächlich eine ziemlich häufig Aufgrund der Natur der Imperativsprachen wird dieses Muster außerhalb der Iteration zu einem Problem. In Haskell/anderen funktionalen Sprachen müssten Sie sich nicht mit diesem Problem befassen. Sie können Elemente gruppieren, basierend auf diesen Gruppen filtern und den richtigen Wert herausholen. In einer imperativen Sprache könnte man das machen, aber man müsste die gesamte Liste vorher parsen, wenn man einen Filter machen möchte. Da Haskell ungenau ausgewertet wird, werden im Gegensatz zu C#, C, C++, Java usw. keine Performance-Hits durchgeführt. – snb