C#: unikaj nieskończonej rekurencji podczas przechodzenia wykresu obiektów
Mam Wykres obiektu, w którym każdy obiekt potomny zawiera właściwość, która odwołuje się do jego rodzica. Czy istnieją jakieś dobre strategie ignorowania referencji nadrzędnych w celu uniknięcia nieskończonej rekurencji? Rozważałem dodanie specjalnego [rodzica] atrybutu do tych właściwości lub użycie specjalnej konwencji nazewnictwa, ale być może istnieje lepszy sposób.
6 answers
Jeśli pętle mogą być uogólnione (możesz mieć dowolną liczbę elementów tworzących pętlę), możesz śledzić obiekty, które już widziałeś w HashSet
i zatrzymać, jeśli obiekt jest już w zestawie podczas jego odwiedzania. Lub dodaj flagę do obiektów, które ustawiasz podczas odwiedzania (ale potem musisz wrócić i wyłączyć wszystkie flagi, gdy skończysz, a Wykres może być przemierzany tylko przez pojedynczy wątek na raz).
Alternatywnie, jeśli pętle zostaną zwrócone tylko do rodzica, może zachować odniesienie do rodzica, a nie pętlę na właściwościach, które odnoszą się do niego.
Dla uproszczenia, jeśli wiesz, że Referencja rodzica będzie miała określoną nazwę, po prostu nie możesz pętli na tej właściwości:)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-05 17:57:33
Co za zbieg okoliczności; to jest temat mojego bloga w najbliższy poniedziałek. Zobacz go po więcej szczegółów. Do tego czasu, oto kod, który da ci pomysł, jak to zrobić:
static IEnumerable<T> Traversal<T>(
T item,
Func<T, IEnumerable<T>> children)
{
var seen = new HashSet<T>();
var stack = new Stack<T>();
seen.Add(item);
stack.Push(item);
yield return item;
while(stack.Count > 0)
{
T current = stack.Pop();
foreach(T newItem in children(current))
{
if (!seen.Contains(newItem))
{
seen.Add(newItem);
stack.Push(newItem);
yield return newItem;
}
}
}
}
Metoda przyjmuje dwie rzeczy: element i relację, która tworzy zbiór wszystkiego, co przylega do elementu. Tworzy ona Depth-first traversal of the transitive and reflexive closure of the adjacency relation on the items . Niech liczba elementów na wykresie będzie n, A maksymalna głębokość wynosi 1
Zauważ, że szczytowe wykorzystanie pamięci tego algorytmu wynosi oczywiście O(n + d) = O (n).
Więc dla przykład:
foreach(Node node in Traversal(myGraph.Root, n => n.Children))
Console.WriteLine(node.Name);
Ma sens?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-14 15:19:39
Jeśli robisz przemierzanie wykresu, możesz mieć flagę" odwiedzony " na każdym węźle. Zapewnia to, że nie odwiedzisz ponownie węzła i prawdopodobnie utkniesz w nieskończonej pętli. Uważam, że jest to standardowy sposób wykonywania grafu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-05 17:08:06
Nie jestem do końca pewien, co próbujesz zrobić tutaj, ale możesz po prostu utrzymać hashtable ze wszystkimi wcześniej odwiedzonymi węzłami, gdy robisz swoje szerokość pierwsze wyszukiwanie głębi pierwsze wyszukiwanie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-05 17:09:38
Jest to częsty problem, ale najlepsze podejście zależy od scenariusza. Dodatkowym problemem jest to, że w wielu przypadkach nie jest to problem dwukrotnego odwiedzenia tego samego obiektu - co nie implikuje rekurencji-na przykład rozważ drzewo:
A
=> B
=> C
=> D
=> C
Może to być poprawne (pomyśl XmlSerializer
, które po prostu zapisywałyby instancję C
dwa razy), więc często konieczne jest wypychanie / pop obiektów na stosie, aby sprawdzić prawdziwą rekurencję. Ostatnim razem, kiedy wprowadziłem "gościa", zachowałem " głębię" licznik, i tylko włączone sprawdzanie stosu poza określonym progiem-oznacza to, że Większość drzew po prostu kończy się wykonywaniem niektórych ++
/--
, ale nic droższego. Możesz zobaczyć podejście, które zastosowałem tutaj .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-05 18:22:51
Opublikowałem post wyjaśniający szczegółowo za pomocą przykładów kodu, jak wykonać object traversal przez rekurencyjne odbicie, a także wykrywać i unikać rekurencyjnych odniesień, aby zapobiec wyjątkowi stosu nad przepływem: https://doguarslan.wordpress.com/2016/10/03/object-graph-traversal-by-recursive-reflection/
W tym przykładzie zrobiłem najpierw głębię, używając rekurencyjnego odbicia i utrzymałem HashSet odwiedzanych węzłów dla typów referencyjnych. Należy uważać, aby zainicjować Twój HashSet z niestandardowym porównaniem Równości, który wykorzystuje odniesienie do obiektu do obliczenia hash, w zasadzie metoda GetHashCode () zaimplementowana przez samą klasę obiektu bazowego, a nie przeciążone wersje GetHashCode (), ponieważ jeśli typy właściwości, które przemierzasz, przeciążają metodę GetHashCode, możesz wykryć fałszywe kolizje hash i pomyśleć, że wykryłeś rekurencyjne odniesienie, które w rzeczywistości może być, że przeciążona wersja GetHashCode produkująca tę samą wartość hash przez niektóre heurystyki i mylące HashSet, wszystko, co musisz wykryć, to sprawdzić, czy w dowolnym miejscu w drzewie obiektów znajduje się jakieś dziecko-rodzic wskazujące na to samo miejsce w pamięci.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-04 08:43:01