2011-09-23 14 views
7

Estoy desarrollando una función optimizada simple JSON. Lua usa tablas para representar matrices pero en JSON necesito reconocerlas. El código siguiente se utiliza:¿Cómo puedo saber si una tabla es una matriz?

t={ 
    a="hi", 
    b=100 
} 

function table2json(t,formatted) 
if type(t)~="table" then return nil,"Parameter is not a table. It is: "..type(t) end 

local ret=""--return value 
local lvl=0 --indentation level 
local INDENT=" " --OPTION: the characters put in front of every line for indentation 
function addToRet(str) if formatted then ret=ret..string.rep(INDENT,lvl)..str.."\n" else ret=ret..str end end 

addToRet("{") 
lvl=1 
for k,v in pairs(t) do 
    local typeof=type(v) 
    if typeof=="string" then 
     addToRet(k..":\""..v.."\"") 
    elseif typeof=="number" then 
     addToRet(k..":"..v) 
    end 
end 
lvl=0 
addToRet("}") 

return ret 
end 

print(table2json(t,true)) 

Como se puede ver en JSON referencia a un object es lo que se llama un table en Lua y es diferente de un array.

La pregunta es ¿cómo puedo detectar si una tabla se está utilizando como una matriz?

  • Una solución, por supuesto, es pasar por todos los pares y ver si solo tienen claves numéricas consecutivas, pero eso no es lo suficientemente rápido.
  • Otra solución es poner una bandera en la tabla que dice que es una matriz, no un objeto.

¿Alguna solución más simple/más inteligente?

+0

Ver: http://stackoverflow.com/questions/6077006/how-can-i-check-if-a-lua-table-contains-only-sequential-numeric-indices/6080274#6080274 – BMitch

Respuesta

1

Gracias. He desarrollado el siguiente código y funciona:

---Checks if a table is used as an array. That is: the keys start with one and are sequential numbers 
-- @param t table 
-- @return nil,error string if t is not a table 
-- @return true/false if t is an array/isn't an array 
-- NOTE: it returns true for an empty table 
function isArray(t) 
    if type(t)~="table" then return nil,"Argument is not a table! It is: "..type(t) end 
    --check if all the table keys are numerical and count their number 
    local count=0 
    for k,v in pairs(t) do 
     if type(k)~="number" then return false else count=count+1 end 
    end 
    --all keys are numerical. now let's see if they are sequential and start with 1 
    for i=1,count do 
     --Hint: the VALUE might be "nil", in that case "not t[i]" isn't enough, that's why we check the type 
     if not t[i] and type(t[i])~="nil" then return false end 
    end 
    return true 
end 
+0

Esto podría ser un poco más rápido si se mantiene un registro de entradas en un metatabla, pero no sería genérico. Sin embargo, sería mucho más rápido para tablas grandes. – tjameson

+0

¿Estás seguro? Si el índice de la tabla no es continua ... – Itachi

+0

entonces no es una matriz. – AlexStack

4

No, no hay una forma incorporada de diferenciar, porque en Lua no hay diferencia.

Hay bibliotecas JSON, lo que probablemente ya lo hacen ya existente (por ejemplo. Lua CJSON.

Otras opciones son

  • dejarlo hasta que el usuario especifique qué tipo es el argumento es, o como qué tipo que querría lo han tratado.
  • han declarado explícitamente matrices disponiendo el __newindex de tal manera que sólo se permiten nuevos índices numéricos y posteriores a utilizar.
+0

Me gusta la solución __newindex. – AlexStack

5

Si desea una solución rápida, sencilla y no intrusiva que funcione más veces de las veces, entonces yo diría que simplemente consulte el índice 1 - si existe, la tabla es una matriz. Claro, no hay garantía, pero en mi experiencia, las tablas rara vez tienen tanto claves numéricas como otras. Si es aceptable que confundas algunos objetos con matrices y si esperas que esto suceda a menudo depende de tu escenario de uso, supongo que no es bueno para la biblioteca general de JSON.

Editar: Para la ciencia, fui a ver cómo Lua CJSON hace las cosas. Pasa por todos los pares y comprueba si todas las claves son números enteros mientras mantiene la clave máxima (la función relevante es lua_array_length). Luego decide si serializar la tabla como una matriz u objeto dependiendo de cuán dispersa es la tabla (la relación es controlada por el usuario), es decir, una tabla con índices 1,2,5,10 probablemente se serializará como una matriz, mientras que una tabla con los índices 1,2,1000000 irán como un objeto. Supongo que esta es realmente una buena solución.

3

@AlexStack

if not t[i] and type(t[i])~="nil" then return false end

Este código es incorrecto, si falla cuando uno de los elemets es false.

> return isArray({"one", "two"}) 
true 
> return isArray({false, true}) 
false 

creo que toda la expresión se puede cambiar a type(t[i]) == nil pero todavía fallará en algunos escenarios porque no va a apoyar los valores nulos.

Una buena manera de ir, creo, está tratando con ipairs o comprobar si #t es igual a count, pero #t devuelve 0 con objetos y count serán cero con matrices vacías, por lo que puede necesitar una comprobación adicional en la parte comienzo de la función, algo así como: if not next(t) then return true.

Como comentario, estoy pegando otra aplicación, que se encuentra en lua-cjson (por Mark Pulford):

-- Determine with a Lua table can be treated as an array. 
-- Explicitly returns "not an array" for very sparse arrays. 
-- Returns: 
-- -1 Not an array 
-- 0 Empty table 
-- >0 Highest index in the array 
local function is_array(table) 
    local max = 0 
    local count = 0 
    for k, v in pairs(table) do 
     if type(k) == "number" then 
      if k > max then max = k end 
      count = count + 1 
     else 
      return -1 
     end 
    end 
    if max > count * 2 then 
     return -1 
    end 

    return max 
end 
0

Escribí esta función para imprimir tablas lua bonitas, y tuvo que resolver el mismo problema . Ninguna de las soluciones aquí explica los casos extremos, como que algunas teclas son números pero otras no. Esto prueba cada índice para ver si es compatible con ser una matriz.

function pp(thing) 
    if type(thing) == "table" then 
     local strTable = {} 
     local iTable = {} 
     local iterable = true 
     for k, v in pairs(thing) do 
      --if the key is a string, we don't need to do "[key]" 
      local key = (((not (type(k) == "string")) and "["..pp(k).."]") or k) 
      --this tests if the index is compatible with being an array 
      if (not (type(k) == "number")) or (k > #thing) or(k < 1) or not (math.floor(k) == k) then 
       iterable = false 
      end 
      local val = pp(v) 
      if iterable then iTable[k] = val end 
      table.insert(strTable, (key.."="..val)) 
     end 
     if iterable then strTable = iTable end 
     return string.format("{%s}", table.concat(strTable,",")) 
    elseif type(thing) == "string" then 
     return '"'..thing..'"' 
    else 
     return tostring(thing) 
    end 
end 
3

El algoritmo más simple para diferenciar entre matrices/no-arrays es éste:

local function is_array(t) 
    local i = 0 
    for _ in pairs(t) do 
     i = i + 1 
     if t[i] == nil then return false end 
    end 
    return true 
end 

Explicación aquí: https://web.archive.org/web/20140227143701/http://ericjmritz.name/2014/02/26/lua-is_array/

Dicho esto, usted todavía tiene problemas con mesas vacías - son ¿"arrays" o "hashes"?

Para el caso particular de la serialización JSON, lo que hago es marcar matrices con un campo en su metatabla.

-- use this when deserializing 
local function mark_as_array(t) 
    setmetatable(t, {__isarray = true}) 
end 

-- use this when serializing 
local function is_array(t) 
    local mt = getmetatable(t) 
    return mt.__isarray 
end 
Cuestiones relacionadas