Raccolta di oggetti che possono decidere di dividersi a metà

5

Mi sono imbattuto in un problema di questo tipo più di una volta e mi piacerebbe conoscere gli approcci consigliati per affrontarlo. In alternativa, mi piacerebbe sapere se ha un nome.

Per concretezza, supponiamo di avere un oggetto Colony, uno dei cui membri è una lista di oggetti Organism. Gli oggetti dell'organismo hanno un metodo di aggiornamento. In determinate condizioni, il comportamento desiderato al momento di chiamare Update è che l'organismo "si spacchi a metà" - cioè, per produrre due nuovi Organismi da inserire nella raccolta, e quindi per farsi rimuovere dalla raccolta e distruggere.

Per rendere le cose interessanti, diciamo che la raccolta è ordinata e vogliamo che i nuovi Organismi siano inseriti nello stesso punto in cui il vecchio Organismo era precedentemente occupato.

Inoltre diciamo che vogliamo fare un ciclo attraverso la raccolta di Organismi che chiamano Aggiornamento su ognuno, e vogliamo farlo in modo tale che Update non venga chiamato sugli Organismi appena creati.

Come variante, forse l'Organismo vuole mantenersi in vita ma inserire la sua prole nella collezione accanto ad essa.

Posso pensare ad alcuni modi per farlo con vari compromessi:

  1. Avere Aggiornamento restituisce un elenco di Organismi, sia esso stesso o un elenco di nuovi. Quindi fai in modo che il loop chiamante Update costruisca una nuova collezione da questi e sostituisca la vecchia collezione con essa alla fine.
  2. Pass Aggiorna alcuni (richiami / puntatori / qualsiasi altra cosa) permettendogli di inserire elementi nella raccolta in modo tale che l'indicatore (iteratore / indice loop / qualunque) venga spostato oltre di essi.
  3. Have Update comunica il suo desiderio di inserire nuovi oggetti nel ciclo chiamante e fa in modo che il codice chiamante si occupi dell'effettivo inserimento.

Ci sono altri modi ragionevoli per farlo? Ci sono vantaggi / svantaggi non ovvi per loro? Hanno nomi? Ci sono modi chiari per eseguirli automaticamente?

    
posta Daniel McLaury 18.03.2017 - 00:42
fonte

3 risposte

1

responsabilità

Questa soluzione non è orientata agli oggetti, ma forse è comunque interessante.

TL; DR

Seguendo la tua prima idea (avere Aggiornamento restituisce un elenco di Organismi), in Haskell puoi

  1. definisce un tipo Organism ,
  2. definisce una funzione di aggiornamento update :: Organism -> [Organism] ,
  3. loop su un elenco organism :: [Organism] con organisms >>= update .

Risposta dettagliata

In Haskell puoi modellare la funzione update come una funzione che produce un elenco di organismi:

data Organism = ...

update :: Organism -> [Organism]

Quindi la funzione update può produrre due nuovi organismi e dimenticare quello vecchio, oppure ripetere il vecchio organismo seguito dai suoi figli:

-- Parent disappears
update x = [child1 x, child2 x]
  where
    child1 x = ...
    child2 x = ...

-- Parent followed by children
update x = [x, child1 x, child2 x]
  where
    child1 x = ...
    child2 x = ...

Ora la tua iterazione si riduce a mappare la funzione update sull'elenco iniziale di organismi (che fornisce un elenco di elenchi di organismi) e quindi appiattendo il risultato utilizzando concat , in questo modo

organisms = [..., ..., ]
newOrganisms = concat (map update organisms)

La combinazione concat - map può essere espressa da un'altra funzione chiamata bind e scritta >>= in Haskell (in Scala, si chiama flatMap ), quindi puoi anche scrivere:

organisms >>= update

Questo itera sopra l'elenco, applica update a ciascun elemento e appiattisce il risultato.

Nota

Se hai bisogno di informazioni su come provare questa soluzione in un interprete Haskell, posso fornire maggiori dettagli.

    
risposta data 18.03.2017 - 09:12
fonte
3

Poiché l'operazione principale qui è l'inserimento di oggetti nel mezzo di una lista in cui hai già un puntatore all'Organismo corretto, un elenco collegato sarebbe uno dei modi più efficaci per implementarlo.

Supponiamo di avere una struttura dati come questa:

class Organism(object):
    def __init__(self):
        # you don't have to have reference to 
        # the Node in the Organism, but having it makes
        # things slightly easier if Organism can only
        # belong to a single LinkedList. If you don't have 
        # a reference to the Node, you'd want to have
        # a mapping/dictionary in the LinkedList object
        # to lookup the Node representing an Organism
        # in that Linked List
        self.node = None

class Node(object):
    def __init__(self, obj):
        self.left = None
        self.right = None
        self.obj = obj
        self.obj.node = self

Per inserire un oggetto nella lista collegata fai:

class Node(object):
    ...
    def insert_left(self, obj):
        node = Node(obj)
        node.left = self.left
        node.right = self
        self.left.right = node
        self.left = node

Per rimuovere un oggetto dalla lista collegata che fai:

class Node(object):
    ...
    def remove(self):
        self.left.right = self.right
        self.right.left = self.left
        self.left = None
        self.right = None

Per dividere un Organismo, fai:

class Organism(object):
    ...
    def mitosis(self):
        c1 = Organism()
        c2 = Organism()
        node = self.node
        node.insert_left(c1)
        node.insert_left(c2)
        node.remove()

Per fare in modo che l'organismo originale sopravviva alla nascita dopo che i suoi figli l'hanno fatto:

class Organism(object):
    ...
    def birth(self):
        c = Organism()
        self.node.insert_left(c)

Per ripetere l'elenco collegato, devi semplicemente passare attraverso i nodi giusti:

class LinkedList(object):
    def __iter__(self):
        node = self.list.head
        while node is not None:
            yield node.obj
            node = node.right

class Colony(object):
    def __init__(self):
        self.list = LinkedList()

    def update(self):
        for node in self.list:
            node.update()

Dato che insert_left () quando Organism split () o birth (), qualsiasi Organismo appena creato non verrà aggiornato nell'iterazione corrente.

Se vuoi che un Organismo sia in grado di decidere se i suoi nuovi figli debbano essere iterati o se vuoi la flessibilità se i nuovi figli sono insert_left () o insert_right () e se l'Organismo appena creato ha ancora bisogno per essere saltato, puoi usare Continuing Passing Style durante l'iterazione e restituire esplicitamente il prossimo Organism per iterare.

    
risposta data 18.03.2017 - 05:08
fonte
1

L'opzione 3 sarà la più semplice, la più fingexable e la più performante. Molto spesso un oggetto aggiorna semplicemente il suo stato interno e "passa" allo split.

Update does not get called on newly-created Organisms.

Questo non sembra avere uno scopo. Vorrai che un oggetto tenga traccia del numero di volte in cui è stato aggiornato comunque (età) e sarebbe più bello avere un nuovo oggetto inizializzarsi sul primo aggiornamento piuttosto che farlo inizializzare dal suo genitore.

È possibile utilizzare un ciclo for e inserire eventuali nuovi oggetti prima del valore iteratore. In questo modo qualsiasi nuovo oggetto sarà il primo a essere colpito per l'aggiornamento.

Un modo pratico sarebbe per un oggetto di divisione restituire un oggetto figlio. La conoscenza del processo di suddivisione e come aggiornare la parte restante dell'oggetto potrebbe rimanere nell'oggetto stesso. Se l'aggiornamento restituisce null, non ci sarebbe nulla da fare (prossimo). Se viene restituito un figlio, il codice di loop potrebbe decidere di inserirlo prima del genitore o in un'altra raccolta.

Supponendo che il processo di aggiornamento dipenda dai vicini di cui si avrebbe bisogno, gli oggetti devono avere riferimenti a quei vicini al momento dell'aggiornamento. I collegamenti nell'oggetto stesso potrebbero servire a tale scopo, ma potresti anche avere riferimenti ai vicini iniettati come argomenti del metodo di aggiornamento. Questo ti farebbe risparmiare molti riferimenti privati.

In alternativa, fai in modo che gli oggetti tengano traccia della loro posizione su un aereo e li alimentino con ogni vicino toccante su ciascun aggiornamento. Programmare un comportamento. Potresti visualizzarlo in una bitmap. Dovrebbe essere divertente.

    
risposta data 18.03.2017 - 10:35
fonte

Leggi altre domande sui tag