向量胶水 |
|
Vectors
的受限语义。 这不一定是最好的代码,也不一定是没有错误的,但它可能对某些人有用。 除了向量之外,它还展示了一些使用函数式编程的例子。 这是从邮件列表中关于 getn
和使用键 n
来保存列表长度(好吧,我会说 Vector
)的讨论中产生的。
[!] 版本通知: 以下代码适用于旧版本的 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
的定义显示了 reduce 的另一种用法。(注意 reduce 运算符是可交换的,如果不是对称的)
> 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