2012-03-07 14 views
5

Quanto segue avrà più senso se hai mai giocato a Minecraft. Dato che molti di voi no, cercherò di spiegarlo nel miglior modo possibileLettura dati ricorsiva in Python

Sto provando a scrivere una funzione ricorsiva in grado di trovare i passaggi per creare qualsiasi oggetto di minecraft da un flatfile di ricette di minecraft. Questo mi ha davvero sconcertato.

Il file flat è un po 'lungo, quindi l'ho incluso in this gist.

def getRecipeChain(item, quantity=1): 
    #magic recursive stuffs go here 

Quindi, in pratica ho bisogno di guardare la prima ricetta poi cercare le ricette per tutti i componenti di tale prima ricetta e così via fino ad arrivare a oggetti con ricette. Ogni volta che ho bisogno di aggiungere la ricetta per una lista in modo da ottenere una sorta di set di istruzioni di ciò al fine di oggetti artigianali in.

Così qui è la funzione che ho ora (quella che il non funziona)

def getRecipeChain(name, quantity=1): 
    chain = [] 

    def getRecipe(name1, quantity1=1): 
     if name1 in recipes: 
      for item in recipes[name1]["ingredients"]["input"]: 
       if item in recipes: 
        getRecipe(item, quantity1) 
       else: 
        chain.append(item) 

    getRecipe(name, quantity) 
    return chain 

Ecco l'output ideale che sto cercando. È un dizionario con il nome dell'articolo e la quantità in esso memorizzati.

>>> getRecipeChain("solar_panel", 1): 
{"insulated_copper_cable":13, "electronic_circuit":2, "re_battery":1, "furnace":1, "machine":1, "generator":1, "solar_panel":1} 

Quindi la domanda è: come faccio?

So che chiedere alla gente di fare del lavoro per voi è accigliato qui, quindi se pensate che questo sia un po 'troppo vicino a voi, semplicemente facendo il codice per me, basta dirlo.

+2

Basta dire, ma penso che l'output del campione non è giusto ... – PearsonArtPhoto

+0

In che modo è corretta? – giodamelio

+2

Beh, insulated_copper_cable non è un articolo base, vero? Né è elettronica_circuito. Sembra che tu voglia ottenere gli ingredienti base, non quelli complessi. – PearsonArtPhoto

risposta

3

Questo può essere elegantemente risolto utilizzando collections.Counter, che supporta oltre:

from collections import Counter 

def getRecipe(name, quantity=1): 
    if not name in recipes: return Counter({name: quantity}) 

    subitems = recipes[name]["ingredients"]["input"] 
    return sum((getRecipe(item, quantity) for item in subitems), 
      Counter()) 

print repr(dict(getRecipe("solar_panel"))) 
# => {'copper': 39, 'refined_iron': 10, 'glass': 3, 
#  'rubber': 78, 'cobblestone': 8, 'tin': 4, 
#  'coal_dust': 3, 'nothing': 10, 'redstone': 6} 
1

Penso che il problema sia di 2 volte. Prima di tutto, è necessario aggiungere gli elementi alla catena nella chiamata ricorsiva a getRecipe(). In secondo luogo, penso che le due funzioni complicano inutilmente le cose. Penso che solo l'interno dovrebbe fare. Qualcosa di simile è ciò che stai cercando. Non l'ho provato, ma dovrebbe essere abbastanza vicino per iniziare sulla giusta strada.

def getRecipe(name, quantity=1): 
    chain=[]; 
    if name in recipes: 
     for item in recipes[name]["ingredients"]["input"]: 
      if item in recipes: 
       chain.append(getRecipe(item, quantity)) 
      else: 
       chain.append(item) 
    return chain 

EDIT: I commenti stanno riempiendo la mia mancanza di conoscenza di Python, quindi ecco una soluzione migliore.

from collections import Counter 
def getRecipe(name, quantity=1, count=Counter()): 
    if name in recipes: 
     for item in recipes[name]["ingredients"]["input"]: 
      if item in recipes: 
       getRecipe(item, quantity,counter) 
      else: 
       counter[item]+=quantity 
    return counter 
+0

L'output desiderato è un dict, quindi invece di chain.append, dovresti fare * chain.setdefault (item, 0) + = quantity * –

+0

@campos: Questo non funzionerà come previsto, il valore non verrà incrementato. Un 'defaultdict (int)' sarebbe la struttura dati ideale qui. –

+0

oops, è vero. –