% CSci 450/503: Programming Languages \  Advanced Lua Functions% H. Conrad Cunningham % 19 September 2016**Acknowledgements:** These slides are adapted from the slide set 6for the course[Programming in Lua](<http://www.dcc.ufrj.br/~fabiom/lua>) created byFabio Mascarenhas, Federal University of Rio de Janeiro, Brazil. Itwas presented at Nankai University, P. R. China, inJuly 2013. Although that course used Lua 5.2, I have attempted toupdate these slides to Lua 5.3.**Advisory**: The HTML version of this document may require use of abrowser that supports the display of MathML. A good choice as ofSeptember 2016 is a recent version of Firefox from Mozilla.# ADVANCED LUA FUNCTIONS {.center} ## Iterating over `...`-   Can collect extra arguments using `...` in table constructor        function add(...)  -- variadic function            local sum = 0             for _, n in ipairs({ ... }) do -- iterate over args                sum = sum + n 			end             return sum         end-   If any extra arguments `nil` then `{ ... }` not array-   Can use `table.pack` function to collect arguments in table        > t = table.pack(1, nil, 3)  -- n set to number of arguments		> for i = 1, t.n do print(t[i]) end 		1 		nil 		3## Function `table.unpack`-   `table.unpack` returns all elements of array in order (called just    `unpack` in Lua 5.1 and earlier)        > print(table.unpack{ 1, 2, 3, 4 })         1       2       3       4 -   Only guaranteed to work for proper arrays (without holes)-   `table.unpack` optionally takes starting and ending indices and    returns all elements in interval regardless of holes        > a = { [2] = 5, [5] = 0 }         > print(table.unpack(a, 1, 5))         nil     5       nil     nil       0 ## "Named" Arguments -   Can simulate named arguments with a record argument        function rename(args)            return os.rename(args.old, args.new) 		end 	-   Can omit parentheses if function call has single record argument    and uses table constructor `{}`        rename{ new = "perm.lua", old = "temp.lua" }     Style note: Can put spaces between function and `{` but better to omit## Lexical Scoping -   Any local variable visible at function definition also visible    inside function (if not shadowed)        function derivative(f, dx)             dx = dx or 1e-4             return function (x)                      -- both f and dx visible here!                      return (f(x + dx) -f(x)) / dx                    end         end -   *Higher-order function* is function that takes/returns function(s)-   `derivative` is higher-order -- both takes & returns        > df = derivative(function (x) return x * x * x end)         > print(df(5))         75.001500009932 ## Closures (1) -   Function *closes over* all local variables in its lexical scope    - These variables neither global nor local to function		- Non-local variables called **upvalues** in Lua		- Function value called **closure**	-   Function can access/modify non-local variables in its closure        function counter() 		   local n = 0            return function ()                     n = n + 1  -- n non-local to returned function                    return n                   end         end ## Closures (2)         function counter() 		   local n = 0            return function ()                     n = n + 1  -- n non-local to returned function                    return n                   end         end-   Each `counter()` call creates new closure with different `n`        > c1 = counter()         > c2 = counter()         > print(c1())         1         > print(c1())         2         > print(c2())         1## Closures and Sharing -   Closures close over the variables themselves, not copies        function counter2() 		    local n = 0			return function (x)                     n = n + (x or 1) -- same non-local n                     return n                    end,  -- returns two result values			       function (x)                      n = n - (x or 1) -- same non-local n                     return n                    end 		end         > inc, dec = counter2()        > print(inc(5))         5         > print(dec(2))         3         > print(inc())         4 -   Only access to `n` through the closures! ## Callbacks -   Can implement lightweight *callbacks* using closures-   Example: `table.sort` takes callback for optional second    argument -- "less-than" predicate for elements        > a = { "Python", "Lua", "C", "JavaScript", "Java", "Lisp" }         > table.sort(a, function (a, b) return a > b end)         > print_array(a)         { Python, Lua, Lisp, JavaScript, Java, C } -   Common in GUI and other event-driven or asynchronous programs## Functional Programming -   Functional programming typically uses higher-order functions and    immutable values-   Lua primarily an imperative language with mutable data structures-   Functional languages commonly use linked lists for sequences-   Lua typically uses arrays for sequences-   Lua can implement common functional programming patterns on arrays    by returning modified copies ## Lua `map` Function-   Map functions abstract a common pattern    -   apply a function to each element of a sequence	-   return result in another sequence-   Can implement `map` function on Lua arrays        function map(f, l)             local nl = {}  -- nl not accessible outside            for i, x in ipairs(l) do                nl[i] = f(x)            end             return nl         end         > a = { 1, 2, 3, 4, 5 }         > b = map(function (x) return x * x end, a)         > print_array(b)         { 1, 4, 9, 16, 25 } ## Lua `filter` Function-   Filter functions abstract another common pattern    -   apply a predicate (boolean function) to each element of a sequence	-   return subsequence of elements that satisfy predicate-   Can implement `filter` function on Lua arrays        function filter(p, l)            local nl = {}  -- nl not accessible outside            for _, x in ipairs(l) do                if p(x) then                    nl[#nl+1] = x                end             end             return nl         end        > a = { 1, 2, 3, 4, 5 }         > b = filter(function (x) return x % 2 == 1 end, a)         > print_array(b)         { 1, 3, 5 }##  Lua Fold Functions (1) -   Fold functions abstract another common pattern    -   reduce a sequence using a binary operation and a seed 	-   return result-   Left fold applies operation to seed and first element, then    applies operation to result and each subsequent element-   Right fold applies operation to last element and seed, then    applies the operation to each previous element and the result##  Lua Fold Functions (2) -   Can implement `foldl` (fold left) and `foldr` (fold right) on Lua    arrays	        function foldl(op, z, l)            for _, x in ipairs(l) do                z = op(z, x) -- z not accessible outside            end             return z         end         function foldr(op, z, l)             for i = #l, 1, -1 do                 z = op(l[i], z) -- z not accessible outside            end             return z         end ## Curried Functions (1) -   Take a proper prefix of arguments and return function that takes    remaining arguments-   Consider curried version of `map`        function map2(f)            return function(l)                      local nl = {}                      for i, x in ipairs(l) do                        nl[i] = f(x)                      end                     return nl                   end		end ## Curried Functions (2)         function map2(f)            return function(l)                      local nl = {}                      for i, x in ipairs(l) do                        nl[i] = f(x)                      end                     return nl                   end		end -   Currying enables partial evaluation of functions         > square = map2(function (x) return x * x end)         > print_array(square{ 1, 5, 9 })         { 1, 25, 81 } ## Quiz What is wrong with the function named below, that turns a function `f`with *positional* arguments *into* a function with *named* arguments?How to fix it?        function named(f, names)    -- names: names to position            return function (args)  -- args: names to values                     local l =					   map(function (name) return args[name] end, names)                      f(table.unpack(l))                    end         end         rename = named(os.rename, { "old", "new" })         rename{ old = "old.txt", new = "new.txt" }## Quiz Solution        function named(f, names)    -- names: name to position            return function (args)  -- args: names to values                     local l = 					   map(function (name) return args[name] end, names)                      f(table.unpack(l)) 					 -- return f(table.unpack(l,1,#names))                    end         end         rename = named(os.rename, { "old", "new" })         rename{ old = "old.txt", new = "new.txt" }