The problem with or and false

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

The problem with or and false

Alexander Gladysh
Hi, all!

Lua 5.0.2

Say, I have three tables, A, B and C. I want user to transparently
read from them as from single table, first looking to A, then to B,
and then to C, and raising error if there is no requested key.

-- One can easily write joinN, but I need
-- optimized version for case with three tables.
join3 = function(A, B, C)
  local t = { }
  setmetatable(
    t, {
     __index = function(t, k)
         return A[k] or B[k] or C[k] or error("Not found" .. k)
      end;
     -- Skip __newindex for clarity.
    }
  return t
end

There is one problem with my code: it will not detect entries with
value 'false'.

If I write the expression directly, it looks somewhat ugly:

local r = A[k] if r == nil then
  r = B[k] if r == nil then
    r = C[k] if r == nil then
      error("Not found")
    end
  end
end
return r

I can put all values to table like this:

first_valid = function(...) local i, r = next(arg); return r end
assert_valid = function(val, err) assert(val ~= nil, err); return val end
...
return assert_valid(first_valid(A[k], B[k], C[k]), "Not found")

But this envolves creation of varargs table, which I'd like to avoid
(and four extra function calls, which is probably ok :) ).

Do anyone know more elegant solution?

Thanks,
Alexander.
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Luiz Henrique de Figueiredo
> Say, I have three tables, A, B and C. I want user to transparently
> read from them as from single table, first looking to A, then to B,
> and then to C, and raising error if there is no requested key.

> Do anyone know more elegant solution?

How about this:

   A= { a=10 }
   B= { b=20 }
   C= { c=30 }

   setmetatable(A, { __index=B} )
   setmetatable(B, { __index=C} )
   setmetatable(C, { __index=error} )

   print(A.a)
   print(A.b)
   print(A.c)
   print(A.z)

--lhf
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Alexander Gladysh
> > Say, I have three tables, A, B and C. I want user to transparently
> > read from them as from single table, first looking to A, then to B,
> > and then to C, and raising error if there is no requested key.
>
> > Do anyone know more elegant solution?
>
> How about this:
>
>   A= { a=10 }
>   B= { b=20 }
>   C= { c=30 }
>
>   setmetatable(A, { __index=B} )
>   setmetatable(B, { __index=C} )
>   setmetatable(C, { __index=error} )
>
>   print(A.a)
>   print(A.b)
>   print(A.c)
>   print(A.z)

Well, I should apologise, as I forgot to mention that I do not want to
modify metatables of original tables. :(

Thank you,
Alexander.
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Mildred Ki'Lya
In reply to this post by Alexander Gladysh

On Wed, 29 Mar 2006 22:10:01 +0400 "Alexander Gladysh"
<[hidden email]> wrote:
> local r = A[k] if r == nil then
>   r = B[k] if r == nil then
>     r = C[k] if r == nil then
>       error("Not found")
>     end
>   end
> end
> return r

Is that code so ugly ?
You also can try :
        for _,r in pairs{ A[k], B[k], C[k]} do
                return r
        end
        return error("Not found")

--
Mildred       <xmpp:[hidden email]> <http://mildred632.free.fr/>
Clef GPG :    <hkp://pgp.mit.edu> ou <http://mildred632.free.fr/gpg_key>
Fingerprint : 197C A7E6 645B 4299 6D37 684B 6F9D A8D6 [9A7D 2E2B]
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Alexander Gladysh
> > local r = A[k] if r == nil then
> >   r = B[k] if r == nil then
> >     r = C[k] if r == nil then
> >       error("Not found")
> >     end
> >   end
> > end
> > return r
>
> Is that code so ugly ?

Well, not *so*, but imperfect enough to ask a 'self-educational' question. :)

> You also can try :
>        for _,r in pairs{ A[k], B[k], C[k]} do
>                return r
>        end
>        return error("Not found")

It probably can be written even shorter as

  local _,r = asser_valid(next { A[k], B[k], C[k] }, "Not found")
  return r

(where assert_valid is described in my first letter).

But it involves table creation anyway. Is there a solution without
table creation?

Thanks,
Alexander.
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Romulo Bahiense
> But it involves table creation anyway. Is there a solution without
> table creation?

Well, you can always write a C function.

/* This function should be called this way:
    local value = findany(A, B, C, k) */
int ll_findany(lua_State *L)
{
     lua_settop(L, 4);
     int i;
     for (i = 1; i < 4; i++)
     {
         lua_pushvalue(L, 4); /* Pushes the key */
         lua_gettable(L, i);
         if (lua_isnil(L, -1))
             lua_pop(L);
         else
             return 1;
     }
     /* You could also raise an error here. */
     return 0;
}

You could put the tables as upvalues, too:

int ll_findany2(lua_State *L)
{
     int i;
     for (i = 1; i < 4; i++)
     {
         lua_pushvalue(1);
         lua_gettable(L, lua_upvalueindex(i));
         if (lua_isnil(L, -1))
             lua_pop(L);
         else
             return 1;
     }
     /* Again, you could raise an error here */
     return 0;
}

But this function must be registered this way:

/* Push the tables to the stack */
lua_newtable(L); /* A */
lua_newtable(L); /* B */
lua_newtable(L); /* C */

/* And push the "closured" function :) */
lua_pushcclosure(L, ll_findany2, 3);

Now you can just write:

local result = findany2(k)

(No need to mention that I haven't tested the code, right?)

--rb
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Gavin Kistner
In reply to this post by Alexander Gladysh
On Mar 29, 2006, at 11:10 AM, Alexander Gladysh wrote:

         return A[k] or B[k] or C[k] or error("Not found" .. k)


How about:

return A[k]~=nil and A[k] or B[k]~=nil and B[k] or C[k]~=nil and C[k] or error( "Not Found" .. k )
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Adrian Sietsma
Gavin Kistner wrote:

> How about:
>
> return A[k]~=nil and A[k] or B[k]~=nil and B[k] or C[k]~=nil and C[k] or
> error( "Not Found" .. k )
still no good... A[k] = false, B[k] = true will return B[k}, not A[k].

You can define a placeholder for false...

local FALSE = {}

then use FALSE for false, and substitute to<->from where required.

ie
set:
t[k] = (v == false) and FALSE or v
get:
return (t[k] ~= FALSE) and t[k]


join3 = function(A, B, C)
   local t = { }
   setmetatable(
     t, {
      __index = function(t, k)
          local r = A[k] or B[k] or C[k]
          return (r == nil) and error("Not found" .. k) or r ~=FALSE and r
       end;
      -- Skip __newindex for clarity. Must substitute false with FALSE
     }
   return t
end


Adrian


Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Alexander Gladysh
> You can define a placeholder for false...
>
> local FALSE = {}
>
> then use FALSE for false, and substitute to<->from where required.
>
> ie
> set:
> t[k] = (v == false) and FALSE or v
> get:
> return (t[k] ~= FALSE) and t[k]

Unfortunately, I need to have all tables in "honest" state, so such
joined usage would be completely transparent for their other users.

Thanks,
Alexander.
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Javier Guerra Giraldez
On Thursday 30 March 2006 9:18 am, Alexander Gladysh wrote:
> > You can define a placeholder for false...
> Unfortunately, I need to have all tables in "honest" state, so such
> joined usage would be completely transparent for their other users.

another solution with a C function... (luaTCC in this case ;-)  ) it returns
the first non-nil parameter:

require "lua_tcc"
getfirst = tcc.compile ([[
  #include "lua.h"

  int getfirst (lua_State *L) {
    int i, n = lua_gettop (L);
               
    for (i = 1; i <= n; i++) {
      if (!lua_isnil (L, i)) {
        lua_settop (L, i);
        return 1;
      }
    }
    return 0;
  }

]], "getfirst")

print (getfirst (nil,1,2))
==> 1
print (getfirst (nil, false,2))
==> 2

use it as:
  return getfirst (A[k], B[k], C[k])


--
Javier

attachment0 (207 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Javier Guerra Giraldez
On Thursday 30 March 2006 9:26 am, Javier Guerra wrote:
> print (getfirst (nil, false,2))
> ==> 2

oops!
it should be :
print (getfirst (nil, false,2))
==> false

(that's the whole point of it!)


--
Javier

attachment0 (207 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: The problem with or and false

Adrian Sietsma
In reply to this post by Alexander Gladysh
Alexander Gladysh wrote:
> Unfortunately, I need to have all tables in "honest" state, so such
> joined usage would be completely transparent for their other users.

how about a generic join ? (not as fast as your function approach, i think)

join = function(...)
   local t1 = arg -- {...} for 5.1
   return setmetatable({},
     {
      __index = function(t, k)
          for tt,i in ipairs(t1) do
             local ret = tt[k]
             if (ret~=nil) then return ret end
          end
          error("Not Found" .. k )
       end;
      -- Skip __newindex for clarity.
     }
end

Adrian.