1. Lua

Lua ist eine einfache, aber leistungsfähige Skriptsprache, die ab UniPlot R2016 parallel zu UniScript verwendet werden kann. UniPlot verwendet LuaJIT (http://www.luajit.org) von Mike Pall.

1.1. Weitere Informationen

Die erste Auflage des Buchs “Programming in Lua” by Roberto Ierusalimschy, Lua.org, December 2003, ISBN 8590379817 ist online verfügbar: http://www.lua.org/pil/contents.html

1.2. Kurzer Vergleich zu UniScript

Es sollen zwei Funktionen verglichen werden, die das größte Element in einem Vektor finden. Zunächst die UniScript-Funktion:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Maximum und Index eines Vektors bestimmen
def maximum(a)
{
    mi = 1;
    m = a[mi];

    for (i in 1:len(a)) {
        if (a[i] > m) {
            mi = i;
            m = a[i];
        }
    }
    return [m, mi];
}

Die Funktion kann in einem UniPlot-Editor unter einem Namen mit der Endung .ic gespeichert werden. Mit dem Befehl UniScript=>Save/Execute oder der Taste F4 kann sie übersetzt werden. Im UniScript-Kommandofenster (Ansicht=>Kommando-Fenster) kann sie nun ausgeführt werden. Mit

* r = rand(1,1e6)

wird ein Vektor mit 1.000.000 Zufallszahlen im Bereich 0 bis 1 erzeugt. tic() startet eine Zeitmessung und toc() gibt die verstrichene Zeit in Millisekunden aus.

* tic(); maximum(r); toc()
   56.1462

Betrachten wir nun die Lua-Funktion.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- Maximum und Index eines Vektors bestimmen
function maximum(a)
    local mi = 1
    local m = a[mi]

    for i = 1, #a do
        if a[i] > m then
            mi = i
            m = a[i]
        end
    end
    return m, mi
end

Die Funktion wird in einer Datei mit der Extension .lua gespeichert.

In der ersten Zeile befindet sich ein Kommentar. Einzeilige Lua-Kommentare beginnen mit den Zeichen -- und enden am Zeilenende.

Anstatt mit def werden Lua-Funktionen mit function markiert. Anweisungen werden in UniScript mit den Zeichen { ... } geschachtelt, in Lua werden end, do-end oder then-end verwendet.

function ... end
for ... do ... end
if ... then ... end

Die erste Anweisung ist in UniScript mi = 1; bzw. in Lua local mi = 1.

In UniScript sind alle Variablen innerhalb von Funktionen lokale Variablen. Sollen globale Variablen in UniScript-Funktionen verwendet werden, müssen sie am Anfang der Funktion mit global deklariert werden.

In Lua müssen alle lokalen Variablen bei der erstmaligen Verwendung in einer Funktion mit local markiert werden, sonst werden sie als globale Variablen verwendet, was meist unerwünscht ist. Siehe auch http://lua-users.org/wiki/LocalByDefault.

Um die Lua-Funktion auszuführen, wird sie zunächst übersetzt (mit F4 im Editor). Anschliessend wird das UniScript-Kommandofenster in den Lua-Mode umgeschaltet. Dazu wird der Befehl .lua im UniScript-Kommandofenster eingegeben.

../../_images/lua-cmd-wnd-maxi.png

Nun können die drei Anweisungen eingegeben werden, um die Funktion zu testen.

1
2
3
4
> a = {}; for i=1,1e6 do a[i] = math.random() end
> up = require"uniplot"
> up.tic(); maximum(a); print(up.toc())
0.95537533378229

Die erste Zeile erzeugt eine Lua-Table mit 1 Millionen Elementen. Dazu wird die random()-Funktion aus der Lua math-Library verwendet, die eine einzelne Zufallszahl erzeugt.

In Zeile 2 wird die uniplot-Libray geladen. Die beiden Funktionen tic() und toc() verwenden die Windows-QueryPerformanceCounter-Funktion mit der Zeiten genau gemessen werden können.

Die Lua-Funktion braucht nur ca. eine Millisekunde während die UniScript-Funktion über 50 ms gedauert hat. Die Gründe sind: 1.) LuaJIT ist sehr ausgefeilt, 2.) UniScript ist ein Interpreter und bei der Lua-Funktion wird durch LuaJIT kompilierter Code erzeugt und ausgeführt.

In der Praxis wird in UniScript für diese Aufgabe eine C-Funktion verwendet. Beenden Sie dazu das Lua-Kommandofenster mit dem Befehl exit. Im UniScript Kommandofenster kann dann die maxi-Funktion aufgerufen werden, die noch ein wenig schneller als die Lua-Funktion ist.

* a = rand(1e6,1);
* tic(); maxi(a); toc()
    0.7638

1.3. Anbindung an UniScript

UniScript-Funktionen werden über die Funktion r = us.call(us_func_name, p1, ...) aufgerufen. Sie befindet sich in der DLL lua_uniscript.dll. Beispiel:

us = require "lua-uniscript"   -- lua-uniscript.dll laden
m = us.call("sin", 1)
print(m)
0.8414709848079

Da eine UniScript-Funktion eine einzelne Zahl zurück liefert, wird sie in eine Lua-Number umgewandelt. Ansonsten wird der UniScript-Wert in einem Lua userdata gespeichert.

x = us.call("linspace", 0, 2*math.pi, 1e5)
print(x)
matrix: 0x22e70660
print(type(x))
userdata

In x befindet sich ein UniScript-Vektor mit 100.000 Punkten.

y = us.call("sin", x)
us.call("plot", x, y)

Normalerweise wird die us.call()-Funktion nicht direkt verwendet, sondern es wird eine Verpackung verwendet. Hier drei Funktionen des Moduls uniplot:

-- uniplot.lua

local us = require "lua-uniscript"
local mat = require "uniplot.matrix"

local u = {}

u.abs = function(a1)
    return mat:new(us.call("abs", a1))
end

u.acos = function(a1)
    return mat:new(us.call("acos", a1))
end

u.acosh = function(a1)
    return mat:new(us.call("acosh", a1))
end

return u

Mit

up = require"uniplot"
mat = require"uniplot.matrix"

können folgende UniScript-Operationen in Lua ausgeführt werden:

UniScript Lua Kommentar
a + b a + b or mat.add(a, b) Die Matritzen müssen gleich groß sein, a oder b kann ein Skalar sein.
a - b a - b or mat.sub(a, b) Subtrahieren.
a * b a * b or mat.mul(a, b) Matrix-Multiplikation
a .* b mat.emul(a, b) Elementweise Multiplikation
a / b a / b or mat.div(a, b)  
a ./ b mat.ediv(a, b) Elementweise Division
a \ b    
a' a:trans() Transponierung.
a.' a:etrans()  
nr(a) a:nr() or mat.nr(a) Anzahl an Zeilen (rows).
nc(a) a:nc() or mat.nc(a) Anzahl an Spalten (columns).
len(a) #a or a:len() or mat.len(a) Länge eines Vektors.
a^-1 or inv(a) a^-1 or a:inv() or mat.inv(a) Inverse einer Matrix.
[1,2,3] mat.matrix{1,2,3} Zeilenvektor
[1;2;3] mat.matrix({1,2,3}):trans() Spaltenvektor erzeugen.
[1,2;3,4] up.reshape(mat.matrix{1,2,3,4}, 2, 2):trans() Matrix
[a, b] mat.hcat(a,b) Matritzen horizontal verketten.
[a; b] mat.vcat(a,b) Matrizen vertikal verketten.
a < b, <= == != > >= mat.lt(a, b), le, eq, ne, gt, ge Vergleichsoperatoren für Matrizen und Vektoren.
a & b, a | b, a@b, ~a, a << b, a >> b mat.band(a, b), bor, xor, not, shl, shr Bitweises and, or, exclusiv or, not, shift left, shift right
1:10, 1:0.5:3, 3:1 mat.range(1, 10), mat.range(1,0.5,3), mat.range(3, 1) Sequenzen [1, 2, .. 10], [1, 1.5, 2, 2.5, 3], [3, 2, 1]
b = a[i;j] b = a:rowcol(i, j)  
a[i;], a[;j] a:row(i), a:col(j)  
a[:] reshape(a, 1, #a)  
c = 1+3i c = complex(1, 3) Komplexe Zahl oder Matrix erzeugen.

1.4. Debuggen von Lua-Programmen

Der UniScript-Debugger wird zur Zeit erweitert um auch Lua-Programme debuggen zu können. Er wird in einigen Monaten fertig gestellt sein.

1.5. Anmerkungen

  • Die LuaJIT-Version ist 2.1-beta vom 2016-01-18 mit -DLUAJIT_ENABLE_LUA52COMPAT eingeschaltet. Die DLL luajit.dll ist mit Microsoft Visual 2015 kompiliert mit der C-Runtime-Library msvcr120.dll.

    Die LuaJIT DLL kann also einfach durch eine selbst erstellte DLL ausgetauscht werden.

id-1010283