2017-09-01 4 views
2

Je travaille avec beaucoup de fichiers de données écrits en Lua. La plupart d'entre eux est écrit de cette manière, un « annuaire téléphonique » comme exemple:Lua, itérateur personnalisé - bonne façon de définir?

data = { 
    -- First Level - country 
    USA = { 
     -- Second level - city 
     Denver = { 
      -- Third level - actual entries 
      {name = 'John', number = '12345'}, 
      -- more entries 
     }, 
     Washington = { 
      {name = 'Ann', number = '54321'}, 
      -- more entries 
     }, 
     -- more cities with entries 
    }, 
    -- more countries with cities and entries 
} 

Donc, le fait que le premier niveau est « Pays » et le second est « City » est implicite, mais il rend les données plus compact.

Maintenant, lors de la recherche de certaines données, je voudrais itérer sur ces données en tant qu'entrées , y compris cette information nivelée et implicite.

-- Coroutine yielding entries including level data 
function corIter(data) 
    for country,l1 in pairs(data) do 
     for city,l2 in pairs(l1) do 
      for _,entry in pairs(l2) do 
       -- Copy the entry 
       local out = {} 
       for k,v in pairs(entry) do 
        out[k] = v 
       end 
       -- Add level properties 
       out.country = country 
       out.city = city 
       coroutine.yield(out) 
      end 
     end 
    end 
end 

-- Iterate over the entries 
local cor = coroutine.create(corIter) 
local _, entry = coroutine.resume(cor, data) 
while entry do 
    -- Handle the entry, has 'name', 'number', 'country' and 'city' keys 
    table.print(entry) -- (custom print function I use) 

    -- Get the next one 
    _, entry = coroutine.resume(cor) 
end 

Mais je pense que cette approche peut être mauvais, car il garde un peu vivant fil entier à itérer sur une table sacrément d'une manière spécifique.

Y a-t-il une autre solution "évidente" à cela? La clé est la performance et la facilité d'utilisation. Je n'ai pas vraiment besoin d'une solution générale (pour un nombre arbitraire de "niveaux" dans la table de données), mais cela ressemble à un hack.

+1

Pourriez-vous donner un exemple de la requête que vous avez à l'esprit? – lhf

Répondre

0
local function phones(d) 
    local cn, c, tn, t, i 
    return 
     function() 
     local a 
     repeat 
      if tn then 
       a, i = t[i], i+1 
       if not a then 
        i, tn, t = 1, next(c, tn) 
       end 
      else 
       cn, c = next(d, cn) 
       i, tn, t = 1, next(c or {}) 
      end 
     until a or not cn 
     return cn, tn, a 
     end 
end 

for country, town, abonent in phones(data) do 
    print(country, town, abonent.name, abonent.number) 
end 
+0

Cela peut être un peu plus compact que j'aime, mais je pense que je peux voir l'essentiel général de la méthode, merci. J'aime aussi le retour multiple plutôt que de fusionner des données comme je l'ai fait. – dzikakulka

0

Vous pouvez créer votre propre itérateur personnalisé dans Lua, pas besoin d'utiliser une coroutine. Les itérateurs sont des fonctions qui, lorsqu'elles sont appelées, renvoient l'élément suivant de votre structure (vous pouvez utiliser n'importe quelle structure).

Itérateur pour votre exemple serait quelque chose comme ça:

function corIter(data) 
local itOut = {}; 
for country,l1 in pairs(data) do 
    for city,l2 in pairs(l1) do 
     for _,entry in pairs(l2) do 
      -- Copy the entry 
      local out = {} 
      for k,v in pairs(entry) do 
       out[k] = v 
      end 
     out.country = country 
     out.city = city 
     table.insert(itOut,out) 
     end 
    end 
end 
local i = 0 
return function() 
    i = i + 1 
    return itOut[i] 
    end 
end 
end 

La fonction anonyme retournée par « corIter » retournera l'élément suivant de vos données. Notez que lorsque nous copions les entrées dans une autre table en utilisant des 'paires' pour les parcourir, rien ne garantit que l'ordre des entrées sera maintenu comme celui d'origine.

Vous pouvez utiliser ce code pour imprimer les entrées:

for entry in corIter(data) do 
    print(entry) -- this is a table 
    for k,v in pairs(entry) do 
    print(k,v) -- these are the actual values 
    end 
end 
+0

Merci beaucoup! Si je comprends bien, cela conserve un doublon de la table de données comme une valeur de la fonction d'itérateur, n'est-ce pas? Tout est très clair et direct, sympa :) – dzikakulka