向量膠水 |
|
向量
的受限語意。這不一定是最佳的程式碼,也不一定是最高防呆的程式碼,但或許對某人而言是有幫助的。除了向量元素之外,它展示了使用函數項目的幾個範例。這是從 getn
的郵件清單討論以及使用金鑰 n
來保留清單的長度所引發的(好吧,我要說是 向量
)。
[!] 版本公告:以下程式碼屬於較舊的 Lua 版本,Lua 4。特定功能採用標記方法(settagmethod
)等,在 Lua 5 中已不存在,但已由元方法取代。
一些範例輸出會出現在最後。閱讀最後一個函數(tmap
)可能會顯示重新定義的 / 和 ^ 運算子的值。
do local TAG = newtag() local bless = function(t) t.n = getn(t) return settag(t, %TAG) end function vector_seti(t, i, v) if type(i) == "number" and i > 0 and i == floor(i) then rawset(t, i, v) if t.n < i then rawset(t, "n", i) end elseif i == "n" and type(v) == "number" and v >= 0 and v == floor(v) then rawset(t, "n", v) else error("Invalid index to vector") end end settagmethod(TAG, "settable", vector_seti) function vector(...) return settag(arg, %TAG) end function vector_concat(t1, t2) if tag(t1) ~= %TAG or tag(t2) ~= %TAG then error("Can't concat a vector with a non-vector") end local t3 = {} for i = 1, t1.n do t3[i] = t1[i] end for i = 1, t2.n do t3[t1.n + i] = t2[i] end return %bless(t3) end settagmethod(TAG, "concat", vector_concat) -- this should be safe from optimizations since 1*t is defined -- to be t. Consequently, it doesn't guarantee to create a -- new vector function vector_repeat(t, n) if tag(t) ~= %TAG then n, t = t, n end if not tonumber(n) then error("Repeat needs a vector and a number") end local tt = {} if n == 1 then return t elseif n > 0 then for j = 1, t.n do local v = t[j] for i = 1, n do tt[(i-1)*t.n + j] = v end end elseif n < 0 then for j = 0, t.n - 1 do local v = t[j + 1] for i = 1, -n do tt[i*t.n - j] = v end end end return %bless(tt) end settagmethod(TAG, "mul", vector_repeat) settagmethod(TAG, "unm", function(t) return vector_repeat(t, -1) end) function vector_reduce(t, fn) if tag(t) ~= %TAG then fn, t = t, fn end if type(fn) ~= "function" then error ("Reduce needs a function of two arguments") end if t.n == 0 then return nil end local val = t[1] for i = 2, t.n do val = fn(val, t[i]) end return val end settagmethod(TAG, "div", vector_reduce) function tjoin(t, str) str = str or " " return t / function(a, b) return a .. %str .. b end end function vector_map(t, fn) if tag(t) ~= %TAG then fn, t = t, fn end if type(fn) ~= "function" then error ("Map needs a function of one argument") end local tt = {} for i = 1, t.n do tt[i] = fn(t[i]) end return %bless(tt) end settagmethod(TAG, "pow", vector_map) function andf(a, b) return a and b end function vectorp(a) return tag(a) == %TAG end function tmap(fn, ...) local targ = %bless(arg) if type(fn) ~= "function" then error("tmap requires a function") end if 0 == andf/ vectorp^targ then error("map only works on vectors") end local tt = {} for i = 1, min/ getn^targ do local sel = function(t) return t[%i] end tt[i] = call(fn, sel^targ) end return %bless(tt) end end
以下是部分範例輸出。函數 p
只是讓觀看結果變得更輕鬆。
> function p(t) print(tjoin(t)) end >
> a = vector("this", "is", "a", "vector") > p(a) this is a vector
> p(2 * a) this is a vector this is a vector > p(a .. a) this is a vector this is a vector
> p(-3 * a) vector a is this vector a is this vector a is this > p(-a) vector a is this
getn
是可行的,但使用 n
金鑰一樣容易。
> print(a.n) 4
我們可以正常使用 tinsert
和 tremove
,並使用整數來索引向量。
> tinsert(a, ".") > print(a.n) 5 > p(a) this is a vector . > tremove(a, 3) > p(a) this is vector . > a[4] = "a" > p(a) this is vector a > a[3], a[4] = a[4], a[3] > p(a) this is a vector
但向量不喜歡用作表格
> a.note = 27 error: Invalid index to vector stack traceback: 1: function `error' [C] 2: function `vector_seti' at line 16 [file `table.lua'] 3: main of string "a.note = 27" at line 1
我們也可以設定 n
來「截斷」該向量,然後變更它回原來的值來回復被截斷的值。
> a.n = 2 > p(a) this is > a.n = 3 > p(a) this is a > a.n = 4 > p(a) this is a test
> a.n = 5 > p(a) error: attempt to concat local `b' (a nil value) stack traceback: 1: function `fn' at line 82 [file `table.lua'] 2: function `vector_reduce' at line 73 [file `table.lua'] 3: function `tjoin' at line 82 [file `table.lua'] 4: function `p' at line 116 [file `table.lua'] 5: main of string "p(a)" at line 1
> p(strupper^ a) THIS IS A TEST > b = vector(4, 8, -3.4, 7) > print (max/ b) 8
不幸地,我們自己定義基元
> function sum(a, b) return a + b end > print (sum/ b) 15.6
因此,擁有周圍的一些功能運算子非常得心應手
> function bind2(fn, b) return function(a) return %fn(a, %b) end end > add1 = bind2(sum, 1) > p(add1^ b) 5 9 -2.4 8 > function average(a) return (sum/ a) / a.n end > print(average(b)) 3.9
tmap
被定義為允許對多個參數進行對應
> function ratio(a, b) return a / b end > p(tmap(ratio, b, add1^ b)) 0.8 0.8888888888888888 1.416666666666667 0.875
tjoin
定義顯示了簡化的另一個用途。(請注意,簡化運算子是可交換的,如果並非對稱的話)
> p(a) this is a test > print(tjoin(a, ", ")) this, is, a, test > print(tjoin(a, "\n")) this is a test
但是這個小功能或許也會有所用...
> function joiner(s) return function(a, b) return a .. %s .. b end end > print(joiner("\n")/ tmap(joiner("\t"), a, b)) this 4 is 8 a -3.4 test 7
並延續那個主題,以下是可能會顯示個中的部分程式碼
> function tab2vec(t) \ local v = vector() \ for key, val in t do tinsert(v, vector(key, val)) end \ return v \ end > family = {juan = 23, marie = 16, alfonso = 45, silvana = 42} > -- sort by age > sort(ft, function(a, b) return a[2] < b[2] end) > print(joiner("\n")/ bind2(tjoin, "\t")^ ft) marie 16 juan 23 silvana 42 alfonso 45 > -- sort by name > sort(ft, function(a, b) return a[1] < b[1] end) > print(joiner("\n")/ bind2(tjoin, "\t")^ ft) alfonso 45 juan 23 marie 16 silvana 42