J'ai trouvé une excellente réponse sur les boucles imbriquées paramétriques - Boucles imbriquées paramétriques en Python Cependant, mon exigence est un peu plus difficile
Problème :
Il s'agit d'une boucle imbriquée à 3 profondeurs (je voudrais une fonction qui a n- depth):
for i in range(100 + 1): for j in range(i, 100 + 1): for k in range(j, 100 + 1): -> need to retrieve [i,j,k]
Notez que le point de départ de chaque boucle est dynamique et change avec chaque boucle parente
4 Réponses :
Cela peut être fait de manière récursive (comme vous l'avez déjà deviné), par exemple de cette manière:
def nest_gen(count, start=0, end=101): if count < 1: return elif count == 1: yield from ((r,) for r in range(start, end)) return for i in range(start, end): yield from ((i,) + r for r in nest_gen(count - 1, i, end)) print(tuple(nest_gen(6, end=5)))
J'aime cette solution plus que ma réponse - c'est exactement la même logique, mais beaucoup plus pythonique
Fonction non totale. Provoque un débordement de pile lorsque count = 0
.
Vous êtes le bienvenu mais le codomain n'est toujours pas total. Veuillez consulter le commentaire que j'ai fait sur le post de Green Cloak pour plus de détails.
C'est le genre de chose pour laquelle j'utiliserais la récursivité - comme je vois que vous l'avez noté dans votre balise. Quelque chose comme ça, par exemple:
[(0,0,0), (0,0,1), (0,0,2), ..., (0,0,100), (0,1,1), ..., (0,100,100), (1,1,1), ..., (99,99,99), (100,100,100)]
qui, lorsqu'il est appelé comme iterate (3)
devrait produire
def iterate(depth, i=0, maxrange=101): if depth <= 0: return (yield ()) for j in range(i, maxrange): # for each value of j... if depth == 1: # base case: yield (j,) # return a 1-tuple else: # recursive case: for k in iterate(depth-1, j, maxrange): # get a generator for the next level of recursion yield (j,) + k # yield all (depth+1)-tuples from prepending j to the next layer of recursion
p>
erreur de syntaxe sur votre première ligne, :
manquant. le programme donne un mauvais résultat lorsque depth = 0
est spécifié.
Merci de l'avoir signalé, j'ai fait de mon mieux pour y remédier
c'est proche mais le codomain (sorties possibles) est un peu off. Vous voulez probablement return (yield ())
sinon depth = 0
donne aucun résultat au lieu d'un résultat vide . Pour comprendre pourquoi c'est un problème, imaginez si subtract (5,5)
ne renvoie rien ( None
) au lieu de 0
, le "vide" numéro. La fonction dépendant de la sortie de soustraire
devrait vérifier si elle a reçu une valeur. En renvoyant une valeur "vide", l'appelant de soustraire
est assuré qu'un résultat sera toujours retourné.
@ user633183 Merci, je l'ai changé en conséquence. Le retour de tuple ()
fonctionnerait-il également?
return (yield tuple ())
serait équivalent, oui.
@ user633183 Ce qu'un retour
s d'un générateur peut être complètement différent (et est généralement moins intéressant que) ce qu'il produit
s. Je ne comprends pas pourquoi il doit répondre à une exigence particulière ici.
Voici une approche itérative:
[0, 0, 0] [0, 0, 1] [0, 0, 2] [0, 0, 3] [0, 1, 1] [0, 1, 2] [0, 1, 3] [0, 2, 2] [0, 2, 3] [0, 3, 3] [1, 1, 3] [1, 2, 2] [1, 2, 3] [1, 3, 3] [2, 2, 3] [2, 3, 3] [3, 3, 3]
un exemple:
for i in iterate(4, 3): print(i)
donne:
def iterate(max_range, dim): if dim == 0: #handle edge case yield from iter(()) elif dim == 1: yield [0] else: fields = [0]*dim while True: yield fields fields[-1] += 1 for i in reversed(range(1, dim)): if fields[i] >= max_range: fields[i - 1] += 1 fields[i] = min(fields[i - 1], max_range -1) if fields[0] == max_range: break
Je ne pense pas que cela corresponde aux exigences du PO. Le niveau d'imbrication et la plage max sont deux variables distinctes.
Belle mise à jour mais un problème demeure. Si dim = 0
votre programme renvoie une erreur au lieu de renvoyer un résultat valide. Voir les commentaires dans la réponse de GreenCloak pour plus de détails.
Si vous considérez dim = 0 comme une entrée valide, vous avez raison. Mais il ne devrait pas être trop difficile de vérifier dim == 0 et de renvoyer un itérateur vide ou tout ce qui est considéré comme significatif dans ce cas.
Voici une approche récursive utilisant un argument par défaut. Les points numérotés ci-dessous font référence aux commentaires numérotés dans le code.
comb
x
d'une plage, déléguez au générateur récursif en utilisant x
comme nouveau point de départ pour la plage imbriquée et ajoutez x
à la combinaison, comb
. for p in nested_range (5, 0, 3): print(p) # (0, 0, 0, 0, 0) # (0, 0, 0, 0, 1) # (0, 0, 0, 0, 2) # (0, 0, 0, 1, 1) # (0, 0, 0, 1, 2) # (0, 0, 0, 2, 2) # (0, 0, 1, 1, 1) # (0, 0, 1, 1, 2) # (0, 0, 1, 2, 2) # (0, 0, 2, 2, 2) # (0, 1, 1, 1, 1) # (0, 1, 1, 1, 2) # (0, 1, 1, 2, 2) # (0, 1, 2, 2, 2) # (0, 2, 2, 2, 2) # (1, 1, 1, 1, 1) # (1, 1, 1, 1, 2) # (1, 1, 1, 2, 2) # (1, 1, 2, 2, 2) # (1, 2, 2, 2, 2) # (2, 2, 2, 2, 2)
Voici une plage imbriquée de trois (3) niveaux de profondeur -
for p in nested_range (0, 0, 4): print(p) # ()
Cette implémentation est une fonction totale et fournit un résultat valide lorsque depth = 0
-
for p in nested_range (3, 0, 4): print(p) # (0, 0, 0) # (0, 0, 1) # (0, 0, 2) # (0, 0, 3) # (0, 1, 1) # (0, 1, 2) # (0, 1, 3) # (0, 2, 2) # (0, 2, 3) # (0, 3, 3) # (1, 1, 1) # (1, 1, 2) # (1, 1, 3) # (1, 2, 2) # (1, 2, 3) # (1, 3, 3) # (2, 2, 2) # (2, 2, 3) # (2, 3, 3) # (3, 3, 3)
Et pour faire bonne mesure, voici la sortie d'une plage imbriquée de cinq (5) niveaux de profondeur - p>
def nested_range (depth = 0, start = 0, end = 1, comb = ()): if depth == 0: yield comb #1 else: for x in range(start, end): #2 yield from nested_range(depth - 1, x, end, comb + (x,))
Je ne suis pas sûr de comprendre, pourquoi pas
print (i, j, k)
?@ olinox14 La question porte sur l'imbrication profonde arbitraire, le code à 3 profondeurs est un exemple.