Functional Library

lua-users home
wiki

 -- Functional Library
 --
 -- @file    functional.lua
 -- @author  Shimomura Ikkei
 -- @date    2005/05/18
 --
 -- @brief    porting several convenience functional utilities form Haskell,Python etc..

 -- map(function, table)
 -- e.g: map(double, {1,2,3})    -> {2,4,6}
 function map(func, tbl)
     local newtbl = {}
     for i,v in pairs(tbl) do
         newtbl[i] = func(v)
     end
     return newtbl
 end

 -- filter(function, table)
 -- e.g: filter(is_even, {1,2,3,4}) -> {2,4}
 function filter(func, tbl)
     local newtbl= {}
     for i,v in pairs(tbl) do
         if func(v) then
	     newtbl[i]=v
         end
     end
     return newtbl
 end

 -- head(table)
 -- e.g: head({1,2,3}) -> 1
 function head(tbl)
     return tbl[1]
 end

 -- tail(table)
 -- e.g: tail({1,2,3}) -> {2,3}
 --
 -- XXX This is a BAD and ugly implementation.
 -- should return the address to next porinter, like in C (arr+1)
 function tail(tbl)
     if table.getn(tbl) < 1 then
         return nil
     else
         local newtbl = {}
         local tblsize = table.getn(tbl)
         local i = 2
         while (i <= tblsize) do
             table.insert(newtbl, i-1, tbl[i])
             i = i + 1
         end
        return newtbl
     end
 end

 -- foldr(function, default_value, table)
 -- e.g: foldr(operator.mul, 1, {1,2,3,4,5}) -> 120
 function foldr(func, val, tbl)
     for i,v in pairs(tbl) do
         val = func(val, v)
     end
     return val
 end

 -- reduce(function, table)
 -- e.g: reduce(operator.add, {1,2,3,4}) -> 10
 function reduce(func, tbl)
     return foldr(func, head(tbl), tail(tbl))
 end

 -- curry(f,g)
 -- e.g: printf = curry(io.write, string.format)
 --          -> function(...) return io.write(string.format(unpack(arg))) end
 function curry(f,g)
     return function (...)
         return f(g(unpack(arg)))
     end
 end

 -- bind1(func, binding_value_for_1st)
 -- bind2(func, binding_value_for_2nd)
 -- @brief
 --      Binding argument(s) and generate new function.
 -- @see also STL's functional, Boost's Lambda, Combine, Bind.
 -- @examples
 --      local mul5 = bind1(operator.mul, 5) -- mul5(10) is 5 * 10
 --      local sub2 = bind2(operator.sub, 2) -- sub2(5) is 5 -2
 function bind1(func, val1)
     return function (val2)
         return func(val1, val2)
     end
 end
 function bind2(func, val2) -- bind second argument.
     return function (val1)
         return func(val1, val2)
     end
 end

 -- is(checker_function, expected_value)
 -- @brief
 --      check function generator. return the function to return boolean,
 --      if the condition was expected then true, else false.
 -- @example
 --      local is_table = is(type, "table")
 --      local is_even = is(bind2(math.mod, 2), 1)
 --      local is_odd = is(bind2(math.mod, 2), 0)
 is = function(check, expected)
     return function (...)
         if (check(unpack(arg)) == expected) then
             return true
         else
             return false
         end
     end
 end

 -- operator table.
 -- @see also python's operator module.
 operator = {
     mod = math.mod;
     pow = math.pow;
     add = function(n,m) return n + m end;
     sub = function(n,m) return n - m end;
     mul = function(n,m) return n * m end;
     div = function(n,m) return n / m end;
     gt  = function(n,m) return n > m end;
     lt  = function(n,m) return n < m end;
     eq  = function(n,m) return n == m end;
     le  = function(n,m) return n <= m end;
     ge  = function(n,m) return n >= m end;
     ne  = function(n,m) return n ~= m end;

 }

 -- enumFromTo(from, to)
 -- e.g: enumFromTo(1, 10) -> {1,2,3,4,5,6,7,8,9}
 -- TODO How to lazy evaluate in Lua? (thinking with coroutine)
 enumFromTo = function (from,to)
     local newtbl = {}
     local step = bind2(operator[(from < to) and "add" or "sub"], 1)
     local val = from
     while val <= to do
         table.insert(newtbl, table.getn(newtbl)+1, val)
         val = step(val)
     end
     return newtbl
 end

 -- make function to take variant arguments, replace of a table.
 -- this does not mean expand the arguments of function took,
 -- it expand the function's spec: function(tbl) -> function(...)
 function expand_args(func)
     return function(...) return func(arg) end
 end

Sample codes

 local product = bind1(reduce, operator.mul)
 local sum = expand_args(bind1(reduce, operator.add))

 -- @see Python: fact=lambda num: reduce((lambda n,m: n*m), xrange(num,0,-1))
 -- @see Haskell: fact = (foldr (*) 1) . (enumFromTo 1)
 local factorial = curry(product,bind1(enumFromTo,1))


See also: LuaFunctors
RecentChanges · preferences
edit · history
Last edited February 18, 2009 3:44 pm GMT (diff)