Kodomo

Пользователь

Занятие 8 мая 2015 г.

21 или 22 мая будет Митап с докладами по Lua на RIT Fest.

План занятия

Метатаблицы (Lua)

Every value in Lua can have a metatable. This metatable is an ordinary Lua table that defines the behavior of the original value under certain special operations. You can change several aspects of the behavior of operations over a value by setting specific fields in its metatable. For instance, when a non-numeric value is the operand of an addition, Lua checks for a function in the field __add in its metatable. If it finds one, Lua calls this function to perform the addition.

We call the keys in a metatable events and the values metamethods. In the previous example, the event is "add" and the metamethod is the function that performs the addition.

You can query the metatable of any value through the getmetatable function.

You can replace the metatable of tables through the setmetatable function. You cannot change the metatable of other types from Lua (except by using the debug library); you must use the C API for that.

Tables and full userdata have individual metatables (although multiple tables and userdata can share their metatables). Values of all other types share one single metatable per type; that is, there is one single metatable for all numbers, one for all strings, etc.

A metatable controls how an object behaves in arithmetic operations, order comparisons, concatenation, length operation, and indexing. A metatable also can define a function to be called when a userdata is garbage collected. For each of these operations Lua associates a specific key called an event. When Lua performs one of these operations over a value, it checks whether this value has a metatable with the corresponding event. If so, the value associated with that key (the metamethod) controls how Lua will perform the operation.

Metatables control the operations listed next. Each operation is identified by its corresponding name. The key for each operation is a string with its name prefixed by two underscores, __; for instance, the key for operation "add" is the string __add. The semantics of these operations is better explained by a Lua function describing how the interpreter executes the operation.

Генерация и перехват ошибок (Lua)

Lua code can explicitly generate an error by calling the error function. If you need to catch errors in Lua, you can use the pcall function.

Копрограммы (Lua)

You create a coroutine with a call to coroutine.create. Its sole argument is a function that is the main function of the coroutine. The create function only creates a new coroutine and returns a handle to it (an object of type thread); it does not start the coroutine execution.

When you first call coroutine.resume, passing as its first argument a thread returned by coroutine.create, the coroutine starts its execution, at the first line of its main function. Extra arguments passed to coroutine.resume are passed on to the coroutine main function. After the coroutine starts running, it runs until it terminates or yields.

A coroutine can terminate its execution in two ways: normally, when its main function returns (explicitly or implicitly, after the last instruction); and abnormally, if there is an unprotected error. In the first case, coroutine.resume returns true, plus any values returned by the coroutine main function. In case of errors, coroutine.resume returns false plus an error message.

A coroutine yields by calling coroutine.yield. When a coroutine yields, the corresponding coroutine.resume returns immediately, even if the yield happens inside nested function calls (that is, not in the main function, but in a function directly or indirectly called by the main function). In the case of a yield, coroutine.resume also returns true, plus any values passed to coroutine.yield. The next time you resume the same coroutine, it continues its execution from the point where it yielded, with the call to coroutine.yield returning any extra arguments passed to coroutine.resume.

Like coroutine.create, the coroutine.wrap function also creates a coroutine, but instead of returning the coroutine itself, it returns a function that, when called, resumes the coroutine. Any arguments passed to this function go as extra arguments to coroutine.resume. coroutine.wrap returns all the values returned by coroutine.resume, except the first one (the boolean error code). Unlike coroutine.resume, coroutine.wrap does not catch errors; any error is propagated to the caller.

As an example, consider the following code:

     function foo (a)
       print("foo", a)
       return coroutine.yield(2*a)
     end
     
     co = coroutine.create(function (a,b)
           print("co-body", a, b)
           local r = foo(a+1)
           print("co-body", r)
           local r, s = coroutine.yield(a+b, a-b)
           print("co-body", r, s)
           return b, "end"
     end)
            
     print("main", coroutine.resume(co, 1, 10))
     print("main", coroutine.resume(co, "r"))
     print("main", coroutine.resume(co, "x", "y"))
     print("main", coroutine.resume(co, "x", "y"))

When you run it, it produces the following output:

     co-body 1       10
     foo     2
     
     main    true    4
     co-body r
     main    true    11      -9
     co-body x       y
     main    true    10      end
     main    false   cannot resume dead coroutine

Домашнее задание

1. Напишите на Lua функцию, принимающую две строки одинаковой длины и возвращающую строку той же длины, в которой на i-ой позиции стоит "+", если i-ые символы двух строк совпадают, иначе "-".

Пример теста для функции:

local function markEqual(a, b)
  -- здесь реализуйте функцию
end

assert(markEqual("aaa", "aba") == "+-+")

Функция должна кидать ошибку, если тип входных аргументов - не строка, или если длины строк не совпадают.

2. Напишите такую же функцию на C.

Пример теста для функции:

#include <assert.h>
#include <string.h>

void markEqual(char* result, const char* a, const char* b) {
  // здесь реализуйте функцию
}

int main() {
  char result[100];
  markEqual(result, "aaa", "aba");
  assert(strcmp(result, "+-+") == 0);
}

Функция должна с помощью assert проверять равенство длин входных строк.

3. Задача на сопрограммы. Напишите функцию g, которая принимает имя входного файла и возвращает итератор, который проходит по всем натуральным числам этого файла. Будем считать, что натуральное число задается маской %d+

Пример входного файла (1.txt:

  7 а
5  8  
  9 е
 10  
 11 E 57
 12 к

Пример использования функции g:

local function g(filename)
  -- здесь ваше решение
end

for number in g("1.txt") do
  print(number)
end
-- prints 7 5 8 9 10 11 57 12