68 Commits

Author SHA1 Message Date
rxi
c6ab30cf7d Version 2.0.0 2015-01-24 15:21:35 +00:00
rxi
c07194490b Updated license copyright year 2015-01-24 15:20:58 +00:00
rxi
5305cb34c8 Lua 5.3 compatibility fixes 2015-01-12 19:51:24 +00:00
rxi
f7b3eb64bb Fixed typo in lume.sort()'s README section 2015-01-11 15:27:06 +00:00
rxi
edd7c99382 Added lume.ripairs() function, updated README and tests 2015-01-11 14:58:57 +00:00
rxi
c773acdcf4 Removed retainkeys argument from lume.set() 2015-01-11 14:33:56 +00:00
rxi
756d30416d Added lume.wordwrap(); updated README.md and tests 2015-01-10 21:53:52 +00:00
rxi
9b5abf3d58 Changed lume.merge() functionality, updated README and doc
- The old functionality of lume.merge() is now spread across
  lume.merge() (for tables) and lume.concat() (for arrays). Both these
  functions create new tables rather than operating in-place
- Changed lume.fn() to use lume.concat() internally
2015-01-10 17:31:35 +00:00
rxi
397dce4c5e Added lume.concat(), updated README and tests 2015-01-10 17:13:15 +00:00
rxi
ce86f4b0ea Fixed nil iterator in internal getiter() 2015-01-10 17:08:14 +00:00
rxi
c565f0739b Fixed lume.call() section of README.md 2015-01-10 16:56:32 +00:00
rxi
7a0f5a0831 Added lume.sort(), updated tests & README 2015-01-10 16:54:23 +00:00
rxi
b873c043c5 Added lume.call(), updated tests and README 2015-01-10 16:29:13 +00:00
rxi
4373a202dc Changed lume.shuffle() to not operate in-place, updated doc + tests 2015-01-10 16:24:31 +00:00
rxi
ac10d54b47 Changed internal func isarray() to handle nil 2015-01-10 14:32:58 +00:00
rxi
067e58b30f Changed lume.count() to return length if table is array 2015-01-10 14:31:55 +00:00
rxi
a42cbb12f0 Changed all applicable functions to use iter func based on table 2015-01-10 14:23:25 +00:00
rxi
0c21cfcf5b Updated lume.each() to use iter func based on table type 2015-01-10 14:13:07 +00:00
rxi
0e4256ef0d Version 1.5.0 2014-12-21 12:25:38 +00:00
rxi
bcad07fc5f Added explanation of calling lume() to the README 2014-12-13 01:00:47 +00:00
rxi
8f1267f967 Added lume.first() and .last(), updated README.md and tests 2014-12-12 21:33:41 +00:00
rxi
de37bd6d65 Added lume.keys() and tests, updated docs 2014-12-12 20:40:25 +00:00
rxi
ac920c8f5e Fixed lume.invert test name 2014-12-12 20:28:48 +00:00
rxi
5d258d4fd0 Made calling lume table an alias to calling lume.chain()
Updated tests
2014-12-12 20:25:54 +00:00
rxi
df26e7939d Added tests for funcitons which now use iteratee() 2014-12-12 20:22:29 +00:00
rxi
16e370cdf0 Changed all applicable functions to use iteratee() internally 2014-12-12 19:45:22 +00:00
rxi
0b991d7ea5 Added identity() and iteratee() local funcs 2014-12-12 18:56:13 +00:00
rxi
f6174a4b68 Version 1.4.1 2014-07-17 22:02:39 +01:00
rxi
d99bf9549a Removed unnecessary pack & unpack from lume.array() 2014-06-16 21:07:40 +01:00
rxi
0cc52cd24e Version 1.4.0 2014-05-18 08:46:28 +01:00
rxi
0a8258d608 Made some minor formatting changes 2014-05-07 08:21:36 +01:00
rxi
95c3f8960d Merge pull request #3 from icrawler/Dev2
Stripped all trailing whitespace, changed ease function, and pre-computed po2
2014-05-07 08:18:22 +01:00
Phoenix Enero
6b73aaa8ad Pre-computed powers of two in lume.rgba 2014-05-07 12:11:29 +08:00
Phoenix Enero
1a82d308af Changed ease function from cosine to cubic 2014-05-07 12:11:26 +08:00
Phoenix Enero
17b58ec63d Stripped all trailing whitespace 2014-05-07 12:11:15 +08:00
rxi
5ab25046a2 Renamed local memoize_nilkey -> memoize_nil 2014-05-01 21:27:57 +01:00
rxi
3f61d823ae Changed position of lume.time() function 2014-05-01 19:23:44 +01:00
rxi
0716caf6a1 Added lume.memoize(), updated README.md and tests 2014-05-01 18:58:29 +01:00
rxi
6a160a3afe Fixed iscallable() to handle the lack of metatable properly 2014-04-28 12:52:29 +01:00
rxi
fff0d780bb Updated README.md and tests for lume.combine() 2014-04-28 12:48:44 +01:00
rxi
283f7ee787 Changed lume.combine() to ignore nil args 2014-04-28 12:46:58 +01:00
rxi
076ca7972c Version 1.3.1 2014-04-23 20:11:00 +01:00
rxi
c2311f9821 Added missing tostring() call on args in lume.trace() 2014-04-19 09:06:33 +01:00
rxi
beced85d6b Updated test for lume.trace() 2014-04-18 19:16:33 +01:00
rxi
8a2765a41b Fixed lume.trace() to handle and print nil argument 2014-04-18 19:15:13 +01:00
rxi
09847bd266 Version 1.3.0 2014-04-17 20:52:54 +01:00
rxi
841cee30e1 Added another test for lume.slice() 2014-04-17 12:30:19 +01:00
rxi
cd3c0a1eea Made callable tables be accepted as functions 2014-04-17 12:13:50 +01:00
rxi
3190d65130 Removed use of math.min and math.max in lume.slice() 2014-04-17 12:07:59 +01:00
rxi
c7471b32fb Changed lume.uuid() to use local math_random() 2014-04-05 23:08:03 +01:00
rxi
bb56f1ce3a Added missing comment for lume.uuid func in test_lua.lua 2014-04-05 16:37:05 +01:00
rxi
098754056a Added tests for lume.chain() 2014-04-05 16:35:53 +01:00
rxi
330779fb0f Added lume.chain(), added section to README.md 2014-04-05 16:30:10 +01:00
rxi
acdb58a447 Added lume.uuid(), tests and README.md section 2014-04-05 14:41:31 +01:00
rxi
88b428cb4d Added lume.count() to README.md 2014-04-05 12:21:36 +01:00
rxi
fdf01937e2 Added lume.count() function and tests 2014-04-05 12:11:41 +01:00
rxi
3a4ce4fe3b Added type check in lume.fn() and additional tests 2014-04-04 18:38:36 +01:00
rxi
1a087efe99 Added lume.match() to README.md 2014-04-04 18:21:21 +01:00
rxi
9a82d6318e Added tests for lume.match() 2014-04-04 18:09:23 +01:00
rxi
30991649f8 Added lume.match() 2014-04-04 18:08:39 +01:00
rxi
dbd93b3861 Moved position of lume.combine() in readme and other files 2014-04-03 20:17:31 +01:00
rxi
8311519e3f Added test for lume.combine() to test_lume.lua 2014-04-03 20:10:08 +01:00
rxi
82c697b08e Added lume.combine() to README.md 2014-04-03 20:09:56 +01:00
rxi
5db6be936a Added lume.combine() function 2014-04-03 20:09:40 +01:00
rxi
335b928cae Removed function in lume.slice()
Moved the function index() from lume.slice()'s body to local var
absindex, this improves the performance of lume.slice() on luajit
significantly and improves it to a lesser extent on non-jit lua.
2014-04-01 20:16:39 +01:00
rxi
94977b4f7e Updated test for lume.trace() 2014-03-30 18:38:45 +01:00
rxi
e6246834b7 Changed lume.trace() to round numbers to 2 decimal places 2014-03-30 18:31:14 +01:00
rxi
9bf2d24ee2 table.unpack() is now used if unpack() is not available. 2014-03-29 10:02:48 +00:00
4 changed files with 604 additions and 65 deletions

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014, rxi Copyright (c) 2015, rxi
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

133
README.md
View File

@@ -38,7 +38,7 @@ lume.lerp(100, 200, .5) -- Returns 150
``` ```
### lume.smooth(a, b, amount) ### lume.smooth(a, b, amount)
Similar to `lume.lerp()` but uses cosine interpolation instead of linear Similar to `lume.lerp()` but uses cubic interpolation instead of linear
interpolation. interpolation.
### lume.pingpong(x) ### lume.pingpong(x)
@@ -75,7 +75,17 @@ lume.weightedchoice({ ["cat"] = 10, ["dog"] = 5, ["frog"] = 0 })
``` ```
### lume.shuffle(t) ### lume.shuffle(t)
Shuffles the values of array `t` in place, returns the array. Returns a shuffled copy of the array `t`.
### lume.sort(t [, comp])
Returns a copy of the array `t` with all its items sorted. If `comp` is a
function it will be used to compare the items when sorting. If `comp` is a
string it will be used as the key to sort the items by.
```lua
lume.sort({ 1, 4, 3, 2, 5 }) -- Returns { 1, 2, 3, 4, 5 }
lume.sort({ {z=2}, {z=3}, {z=1} }, "z") -- Returns { {z=1}, {z=2}, {z=3} }
lume.sort({ 1, 3, 2 }, function(a, b) return a > b end) -- Returns { 3, 2, 1 }
```
### lume.array(...) ### lume.array(...)
Iterates the supplied iterator and returns an array filled with the values. Iterates the supplied iterator and returns an array filled with the values.
@@ -125,10 +135,8 @@ an error is raised,
lume.reduce({1, 2, 3}, function(a, b) return a + b end) -- Returns 6 lume.reduce({1, 2, 3}, function(a, b) return a + b end) -- Returns 6
``` ```
### lume.set(t [, retainkeys]) ### lume.set(t)
Returns a copy of the `t` table with all the duplicate values removed. If Returns a copy of the `t` array with all the duplicate values removed.
`retainkeys` is true the table is not treated as an array and retains its
original keys.
```lua ```lua
lume.set({2, 1, 2, "cat", "cat"}) -- Returns {1, 2, "cat"} lume.set({2, 1, 2, "cat", "cat"}) -- Returns {1, 2, "cat"}
``` ```
@@ -141,12 +149,17 @@ an array and retains its original keys.
lume.filter({1, 2, 3, 4}, function(x) return x % 2 == 0 end) -- Returns {2, 4} lume.filter({1, 2, 3, 4}, function(x) return x % 2 == 0 end) -- Returns {2, 4}
``` ```
### lume.merge(t, t2 [, retainkeys]) ### lume.merge(...)
Merges all the values from the table `t2` into `t` in place. If `retainkeys` is Returns a new table with all the given tables merged together. If a key exists
true the table is not treated as an array and retains its original keys; if `t` in multiple tables the right-most table's value is used.
and `t2` have a conflicting key, the value from `t2` is used.
```lua ```lua
lume.merge({2, 3}, {4, 5}) -- Returns {2, 3, 4, 5} lume.merge({a=1, b=2, c=3}, {c=8, d=9}) -- Returns {a=1, b=2, c=8, d=9}
```
### lume.concat(...)
Returns a new array consisting of all the given arrays concatenated into one.
```lua
lume.concat({1, 2}, {3, 4}, {5, 6}) -- Returns {1, 2, 3, 4, 5, 6}
``` ```
### lume.find(t, value) ### lume.find(t, value)
@@ -156,6 +169,21 @@ exist in the table.
lume.find({"a", "b", "c"}, "b") -- Returns 2 lume.find({"a", "b", "c"}, "b") -- Returns 2
``` ```
### lume.match(t, fn)
Returns the value and key of the value in table `t` which returns true when
`fn` is called on it. Returns `nil` if no such value exists.
```lua
lume.match({1, 5, 8, 7}, function(x) return x % 2 == 0 end) -- Returns 8, 3
```
### lume.count(t [, fn])
Counts the number of values in the table `t`. If a `fn` function is supplied it
is called on each value, the number of times it returns true is counted.
```lua
lume.count({a = 2, b = 3, c = 4, d = 5}) -- Returns 4
lume.count({1, 2, 4, 6}, function(x) return x % 2 == 0 end) -- Returns 3
```
### lume.slice(t [, i [, j]]) ### lume.slice(t [, i [, j]])
Mimics the behaviour of Lua's `string.sub`, but operates on an array rather Mimics the behaviour of Lua's `string.sub`, but operates on an array rather
than a string. Creates and returns a new array of the given slice. than a string. Creates and returns a new array of the given slice.
@@ -163,6 +191,20 @@ than a string. Creates and returns a new array of the given slice.
lume.slice({"a", "b", "c", "d", "e"}, 2, 4) -- Returns {"b", "c", "d"} lume.slice({"a", "b", "c", "d", "e"}, 2, 4) -- Returns {"b", "c", "d"}
``` ```
### lume.first(t [, n])
Returns the first element of an array or nil if the array is empty. If `n` is
specificed an array of the first `n` elements is returned.
```lua
lume.first({"a", "b", "c"}) -- Returns "a"
```
### lume.last(t [, n])
Returns the last element of an array or nil if the array is empty. If `n` is
specificed an array of the last `n` elements is returned.
```lua
lume.last({"a", "b", "c"}) -- Returns "c"
```
### lume.invert(t) ### lume.invert(t)
Returns a copy of the table where the keys have become the values and the Returns a copy of the table where the keys have become the values and the
values the keys. values the keys.
@@ -170,6 +212,9 @@ values the keys.
lume.invert({a = "x", b = "y"}) -- returns {x = "a", y = "b"} lume.invert({a = "x", b = "y"}) -- returns {x = "a", y = "b"}
``` ```
### lume.keys(t)
Returns an array containing each key of the table.
### lume.clone(t) ### lume.clone(t)
Returns a shallow copy of the table `t`. Returns a shallow copy of the table `t`.
@@ -193,6 +238,32 @@ f() -- Prints "Hello"
f() -- Does nothing f() -- Does nothing
``` ```
### lume.memoize(fn)
Returns a wrapper function to `fn` where the results for any given set of
arguments are cached. `lume.memoize()` is useful when used on functions with
slow-running computations.
```lua
fib = lume.memoize(function(n) return n < 2 and n or fib(n-1) + fib(n-2) end)
```
### lume.combine(...)
Creates a wrapper function which calls each supplied argument in the order they
were passed to `lume.combine()`; nil arguments are ignored. The wrapper
function passes its own arguments to each of its wrapped functions when it is
called.
```lua
local f = lume.combine(function(a, b) print(a + b) end,
function(a, b) print(a * b) end)
f(3, 4) -- Prints "7" then "12" on a new line
```
### lume.call(fn, ...)
Calls the given function with the provided arguments and returns its values. If
`fn` is `nil` then no action is performed and the function returns `nil`.
```lua
lume.call(print, "Hello world") -- Prints "Hello world"
```
### lume.time(fn, ...) ### lume.time(fn, ...)
Inserts the arguments into function `fn` and calls it. Returns the time in Inserts the arguments into function `fn` and calls it. Returns the time in
seconds the function `fn` took to execute followed by `fn`'s returned values. seconds the function `fn` took to execute followed by `fn`'s returned values.
@@ -243,6 +314,15 @@ instead of whitespace.
lume.trim(" Hello ") -- Returns "Hello" lume.trim(" Hello ") -- Returns "Hello"
``` ```
### lume.wordwrap(str [, limit])
Returns `str` wrapped to `limit` number of characters per line, by default
`limit` is `72`. `limit` can also be a function which when passed a string,
returns `true` if it is too long for a single line.
```lua
-- Returns "Hello world\nThis is a\nshort string"
lume.wordwrap("Hello world. This is a short string", 14)
```
### lume.format(str [, vars]) ### lume.format(str [, vars])
Returns a formatted string. The values of keys in the table `vars` can be Returns a formatted string. The values of keys in the table `vars` can be
inserted into the string by using the form `"{key}"` in `str`; numerical keys inserted into the string by using the form `"{key}"` in `str`; numerical keys
@@ -266,6 +346,10 @@ Executes the lua code inside `str`.
lume.dostring("print('Hello!')") -- Prints "Hello!" lume.dostring("print('Hello!')") -- Prints "Hello!"
``` ```
### lume.uuid()
Generates a random UUID string; version 4 as specified in
[RFC 4122](http://www.ietf.org/rfc/rfc4122.txt).
### lume.hotswap(modname) ### lume.hotswap(modname)
Reloads an already loaded module in place, allowing you to immediately see the Reloads an already loaded module in place, allowing you to immediately see the
effects of code changes without having to restart the program. `modname` should effects of code changes without having to restart the program. `modname` should
@@ -277,6 +361,17 @@ lume.hotswap("lume") -- Reloads the lume module
assert(lume.hotswap("inexistant_module")) -- Raises an error assert(lume.hotswap("inexistant_module")) -- Raises an error
``` ```
### lume.ripairs(t)
Performs the same function as `ipairs()` but iterates in reverse; this allows
the removal of items from the table during iteration without any items being
skipped.
```lua
-- Prints "3->c", "2->b" and "1->a" on separate lines
for i, v in lume.ripairs({ "a", "b", "c" }) do
print(i .. "->" .. v)
end
```
### lume.rgba(color) ### lume.rgba(color)
Takes the 32bit integer `color` argument and returns 4 numbers, one for each Takes the 32bit integer `color` argument and returns 4 numbers, one for each
channel, with a range of 0 - 255. The returned values can be used as the channel, with a range of 0 - 255. The returned values can be used as the
@@ -285,6 +380,22 @@ arguments to [LÖVE](http://love2d.org)'s setColor() function.
lume.rgba(0xFF304050) -- Returns 48, 64, 80, 255 lume.rgba(0xFF304050) -- Returns 48, 64, 80, 255
``` ```
### lume.chain(value)
Returns a wrapped object which allows chaining of lume functions. The function
result() should be called at the end of the chain to return the resulting
value.
```lua
lume.chain({1, 2, 3, 4})
:filter(function(x) return x % 2 == 0 end)
:map(function(x) return -x end)
:result() -- Returns { -2, -4 }
```
The table returned by the `lume` module, when called, acts in the same manner
as calling `lume.chain()`.
```lua
lume({1, 2, 3}):each(print) -- Prints 1, 2 then 3 on separate lines
```
## License ## License

350
lume.lua
View File

@@ -1,32 +1,80 @@
-- --
-- lume -- lume
-- --
-- Copyright (c) 2014, rxi -- Copyright (c) 2015, rxi
-- --
-- This library is free software; you can redistribute it and/or modify it -- This library is free software; you can redistribute it and/or modify it
-- under the terms of the MIT license. See LICENSE for details. -- under the terms of the MIT license. See LICENSE for details.
-- --
local lume = { _version = "1.2.1" } local lume = { _version = "2.0.0" }
local pairs, ipairs = pairs, ipairs local pairs, ipairs = pairs, ipairs
local type, assert, unpack = type, assert, unpack local type, assert, unpack = type, assert, unpack or table.unpack
local tostring, tonumber = tostring, tonumber local tostring, tonumber = tostring, tonumber
local math_floor = math.floor local math_floor = math.floor
local math_ceil = math.ceil local math_ceil = math.ceil
local math_random = math.random local math_random = math.random
local math_cos = math.cos local math_cos = math.cos
local math_atan2 = math.atan2 local math_atan2 = math.atan2 or math.atan
local math_sqrt = math.sqrt local math_sqrt = math.sqrt
local math_abs = math.abs local math_abs = math.abs
local math_min = math.min
local math_max = math.max
local math_pi = math.pi local math_pi = math.pi
local noop = function()
end
local identity = function(x)
return x
end
local patternescape = function(str) local patternescape = function(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1") return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
end end
local absindex = function(len, i)
return i < 0 and (len + i + 1) or i
end
local iscallable = function(x)
if type(x) == "function" then return true end
local mt = getmetatable(x)
return mt and mt.__call ~= nil
end
local isarray = function(x)
return (x and x[1]) and true or false
end
local iternil = function()
return noop
end
local getiter = function(x)
if x == nil then
return iternil
elseif isarray(x) then
return ipairs
else
return pairs
end
end
local iteratee = function(x)
if x == nil then return identity end
if iscallable(x) then return x end
if type(x) == "table" then
return function(z)
for k, v in pairs(x) do
if z[k] ~= v then return false end
end
return true
end
end
return function(z) return z[x] end
end
function lume.clamp(x, min, max) function lume.clamp(x, min, max)
return x < min and min or (x > max and max or x) return x < min and min or (x > max and max or x)
@@ -50,7 +98,8 @@ end
function lume.smooth(a, b, amount) function lume.smooth(a, b, amount)
local m = (1 - math_cos(lume.clamp(amount, 0, 1) * math_pi)) / 2 local t = lume.clamp(amount, 0, 1)
local m = t * t * (3 - 2 * t)
return a + (b - a) * m return a + (b - a) * m
end end
@@ -101,41 +150,64 @@ end
function lume.shuffle(t) function lume.shuffle(t)
local rtn = {}
for i = 1, #t do for i = 1, #t do
local r = math_random(#t) local r = math_random(i)
t[i], t[r] = t[r], t[i] if r ~= i then
rtn[i] = rtn[r]
end
rtn[r] = t[i]
end end
return t return rtn
end
function lume.sort(t, comp)
local rtn = lume.clone(t)
if comp then
if type(comp) == "string" then
table.sort(rtn, function(a, b) return a[comp] < b[comp] end)
else
table.sort(rtn, comp)
end
else
table.sort(rtn)
end
return rtn
end end
function lume.array(...) function lume.array(...)
local t = {} local t = {}
for x in unpack({...}) do t[#t + 1] = x end for x in ... do t[#t + 1] = x end
return t return t
end end
function lume.each(t, fn, ...) function lume.each(t, fn, ...)
local iter = getiter(t)
if type(fn) == "string" then if type(fn) == "string" then
for _, v in pairs(t) do v[fn](v, ...) end for _, v in iter(t) do v[fn](v, ...) end
else else
for _, v in pairs(t) do fn(v, ...) end for _, v in iter(t) do fn(v, ...) end
end end
return t return t
end end
function lume.map(t, fn) function lume.map(t, fn)
local rtn = {} fn = iteratee(fn)
for k, v in pairs(t) do rtn[k] = fn(v) end local iter = getiter(t)
local rtn = {}
for k, v in iter(t) do rtn[k] = fn(v) end
return rtn return rtn
end end
function lume.all(t, fn) function lume.all(t, fn)
fn = fn or function(x) return x end fn = iteratee(fn)
for k, v in pairs(t) do local iter = getiter(t)
for k, v in iter(t) do
if not fn(v) then return false end if not fn(v) then return false end
end end
return true return true
@@ -143,8 +215,9 @@ end
function lume.any(t, fn) function lume.any(t, fn)
fn = fn or function(x) return x end fn = iteratee(fn)
for k, v in pairs(t) do local iter = getiter(t)
for k, v in iter(t) do
if fn(v) then return true end if fn(v) then return true end
end end
return false return false
@@ -159,52 +232,112 @@ function lume.reduce(t, fn, first)
end end
function lume.set(t, retainkeys) function lume.set(t)
local rtn = {} local rtn = {}
for k, v in pairs(lume.invert(t)) do for k, v in pairs(lume.invert(t)) do
rtn[retainkeys and v or (#rtn + 1)] = k rtn[#rtn + 1] = k
end end
return rtn return rtn
end end
function lume.filter(t, fn, retainkeys) function lume.filter(t, fn, retainkeys)
fn = iteratee(fn)
local iter = getiter(t)
local rtn = {} local rtn = {}
for k, v in pairs(t) do for k, v in iter(t) do
if fn(v) then rtn[retainkeys and k or (#rtn + 1)] = v end if fn(v) then rtn[retainkeys and k or (#rtn + 1)] = v end
end end
return rtn return rtn
end end
function lume.merge(t, t2, retainkeys) function lume.merge(...)
for k, v in pairs(t2) do local rtn = {}
t[retainkeys and k or (#t + 1)] = v for i = 1, select("#", ...) do
local t = select(i, ...)
local iter = getiter(t)
for k, v in iter(t) do
rtn[k] = v
end
end end
return t return rtn
end
function lume.concat(...)
local rtn = {}
for i = 1, select("#", ...) do
local t = select(i, ...)
local iter = getiter(t)
for k, v in iter(t) do
rtn[#rtn + 1] = v
end
end
return rtn
end end
function lume.find(t, value) function lume.find(t, value)
for k, v in pairs(t) do local iter = getiter(t)
for k, v in iter(t) do
if v == value then return k end if v == value then return k end
end end
return nil return nil
end end
function lume.match(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
for k, v in iter(t) do
if fn(v) then return v, k end
end
return nil
end
function lume.count(t, fn)
local count = 0
local iter = getiter(t)
if fn then
fn = iteratee(fn)
for k, v in iter(t) do
if fn(v) then count = count + 1 end
end
else
if isarray(t) then
return #t
end
for k in iter(t) do count = count + 1 end
end
return count
end
function lume.slice(t, i, j) function lume.slice(t, i, j)
local function index(x) return x < 0 and (#t + x + 1) or x end i = i and absindex(#t, i) or 1
i = i and index(i) or 1 j = j and absindex(#t, j) or #t
j = j and index(j) or #t
local rtn = {} local rtn = {}
for i = math_max(i, 1), math_min(j, #t) do for x = i < 1 and 1 or i, j > #t and #t or j do
rtn[#rtn + 1] = t[i] rtn[#rtn + 1] = t[x]
end end
return rtn return rtn
end end
function lume.first(t, n)
if not n then return t[1] end
return lume.slice(t, 1, n)
end
function lume.last(t, n)
if not n then return t[#t] end
return lume.slice(t, -n, -1)
end
function lume.invert(t) function lume.invert(t)
local rtn = {} local rtn = {}
for k, v in pairs(t) do rtn[v] = k end for k, v in pairs(t) do rtn[v] = k end
@@ -212,6 +345,14 @@ function lume.invert(t)
end end
function lume.keys(t)
local rtn = {}
local iter = getiter(t)
for k, v in iter(t) do rtn[#rtn + 1] = k end
return rtn
end
function lume.clone(t) function lume.clone(t)
local rtn = {} local rtn = {}
for k, v in pairs(t) do rtn[k] = v end for k, v in pairs(t) do rtn[k] = v end
@@ -220,10 +361,11 @@ end
function lume.fn(fn, ...) function lume.fn(fn, ...)
local args = {...} assert(iscallable(fn), "expected a function as the first argument")
local args = { ... }
return function(...) return function(...)
local a = lume.merge(lume.clone(args), {...}) local a = lume.concat(args, { ... })
return fn(unpack(a)) return fn(unpack(a))
end end
end end
@@ -239,6 +381,46 @@ function lume.once(fn, ...)
end end
local memoize_fnkey = {}
local memoize_nil = {}
function lume.memoize(fn)
local cache = {}
return function(...)
local c = cache
for i = 1, select("#", ...) do
local a = select(i, ...) or memoize_nil
c[a] = c[a] or {}
c = c[a]
end
c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)}
return unpack(c[memoize_fnkey])
end
end
function lume.combine(...)
local funcs = {}
for i = 1, select("#", ...) do
local fn = select(i, ...)
if fn ~= nil then
assert(iscallable(fn), "expected a function or nil")
funcs[#funcs + 1] = fn
end
end
return function(...)
for _, f in ipairs(funcs) do f(...) end
end
end
function lume.call(fn, ...)
if fn then
return fn(...)
end
end
function lume.time(fn, ...) function lume.time(fn, ...)
local start = os.clock() local start = os.clock()
local rtn = {fn(...)} local rtn = {fn(...)}
@@ -298,10 +480,36 @@ function lume.trim(str, chars)
end end
function lume.wordwrap(str, limit)
limit = limit or 72
local check
if type(limit) == "number" then
check = function(str) return #str >= limit end
else
check = limit
end
local rtn = {}
for j, line in ipairs(lume.split(str, "\n")) do
local str
for i, word in ipairs(lume.split(line)) do
local s = (str and (str .. " ") or "") .. word
if check(s) then
rtn[#rtn + 1] = str
str = word
else
str = s
end
end
rtn[#rtn + 1] = str
end
return table.concat(rtn, "\n")
end
function lume.format(str, vars) function lume.format(str, vars)
if not vars then return str end if not vars then return str end
local f = function(x) local f = function(x)
return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}") return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}")
end end
return (str:gsub("{(.-)}", f)) return (str:gsub("{(.-)}", f))
end end
@@ -309,8 +517,15 @@ end
function lume.trace(...) function lume.trace(...)
local info = debug.getinfo(2, "Sl") local info = debug.getinfo(2, "Sl")
local head = "[" .. info.short_src .. ":" .. info.currentline .. "] " local t = { "[" .. info.short_src .. ":" .. info.currentline .. "]" }
print(head .. table.concat(lume.map({...}, tostring), " ")) for i = 1, select("#", ...) do
local x = select(i, ...)
if type(x) == "number" then
x = string.format("%g", lume.round(x, .01))
end
t[#t + 1] = tostring(x)
end
print(table.concat(t, " "))
end end
@@ -319,11 +534,21 @@ function lume.dostring(str)
end end
function lume.uuid()
local fn = function(x)
local r = math_random(16) - 1
r = (x == "x") and (r + 1) or (r % 4) + 9
return ("0123456789abcdef"):sub(r, r)
end
return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
end
function lume.hotswap(modname) function lume.hotswap(modname)
local oldglobal = lume.clone(_G) local oldglobal = lume.clone(_G)
local updated = {} local updated = {}
local function update(old, new) local function update(old, new)
if updated[old] then return end if updated[old] then return end
updated[old] = true updated[old] = true
local oldmt, newmt = getmetatable(old), getmetatable(new) local oldmt, newmt = getmetatable(old), getmetatable(new)
if oldmt and newmt then update(oldmt, newmt) end if oldmt and newmt then update(oldmt, newmt) end
@@ -343,7 +568,7 @@ function lume.hotswap(modname)
local newmod = require(modname) local newmod = require(modname)
if type(oldmod) == "table" then update(oldmod, newmod) end if type(oldmod) == "table" then update(oldmod, newmod) end
for k, v in pairs(oldglobal) do for k, v in pairs(oldglobal) do
if v ~= _G[k] and type(v) == "table" then if v ~= _G[k] and type(v) == "table" then
update(v, _G[k]) update(v, _G[k])
_G[k] = v _G[k] = v
end end
@@ -355,13 +580,48 @@ function lume.hotswap(modname)
end end
local ripairs_iter = function(t, i)
i = i - 1
local v = t[i]
if v then return i, v end
end
function lume.ripairs(t)
if t == nil then
return noop
end
return ripairs_iter, t, (#t + 1)
end
function lume.rgba(color) function lume.rgba(color)
local a = math_floor((color / 2 ^ 24) % 256) local a = math_floor((color / 16777216) % 256)
local r = math_floor((color / 2 ^ 16) % 256) local r = math_floor((color / 65536) % 256)
local g = math_floor((color / 2 ^ 08) % 256) local g = math_floor((color / 256) % 256)
local b = math_floor((color) % 256) local b = math_floor((color) % 256)
return r, g, b, a return r, g, b, a
end end
local chain_mt = {}
chain_mt.__index = lume.map(lume.filter(lume, iscallable, true),
function(fn)
return function(self, ...)
self._value = fn(self._value, ...)
return self
end
end)
chain_mt.__index.result = function(x) return x._value end
function lume.chain(value)
return setmetatable({ _value = value }, chain_mt)
end
setmetatable(lume, {
__call = function(t, ...)
return lume.chain(...)
end
})
return lume return lume

View File

@@ -109,12 +109,24 @@ end
-- lume.shuffle -- lume.shuffle
tests["lume.shuffle"] = function() tests["lume.shuffle"] = function()
local t = {1, 2, 3, 4, 5} local t = {1, 2, 3, 4, 5}
lume.shuffle(t) t = lume.shuffle(t)
table.sort(t) table.sort(t)
testeq( t, {1, 2, 3, 4, 5} ) testeq( t, {1, 2, 3, 4, 5} )
testeq( lume.shuffle({}), {} ) testeq( lume.shuffle({}), {} )
end end
-- lume.sort
tests["lume.sort"] = function()
local t = { 1, 5, 2, 4, 3 }
local fn = function(a, b) return a > b end
testeq( t == lume.sort(t), false )
testeq( lume.sort(t), { 1, 2, 3, 4, 5 } )
testeq( lume.sort(t, fn), { 5, 4, 3, 2, 1 } )
testeq( t, { 1, 5, 2, 4, 3 } )
local t = { { id = 2 }, { id = 3 }, { id = 1 } }
testeq( lume.sort(t, "id"), { { id = 1 }, { id = 2 }, { id = 3 } })
end
-- lume.array -- lume.array
tests["lume.array"] = function() tests["lume.array"] = function()
local t = lume.array(pairs({a=0, b=0, c=0})) local t = lume.array(pairs({a=0, b=0, c=0}))
@@ -141,6 +153,8 @@ end
tests["lume.map"] = function() tests["lume.map"] = function()
testeq( lume.map({1, 2, 3}, function(x) return x * 2 end), {2, 4, 6} ) testeq( lume.map({1, 2, 3}, function(x) return x * 2 end), {2, 4, 6} )
testeq( lume.map({a=2,b=3}, function(x) return x * 2 end), {a=4,b=6} ) testeq( lume.map({a=2,b=3}, function(x) return x * 2 end), {a=4,b=6} )
local t = {{ id = 10 }, { id = 20 }, { id = 30 }}
testeq( lume.map(t, "id"), { 10, 20, 30 })
end end
-- lume.all -- lume.all
@@ -149,6 +163,10 @@ tests["lume.all"] = function()
testeq( lume.all({true, true, true, true}), true ) testeq( lume.all({true, true, true, true}), true )
testeq( lume.all({2, 3, 4, 5}, function(x) return x % 2 == 0 end), false ) testeq( lume.all({2, 3, 4, 5}, function(x) return x % 2 == 0 end), false )
testeq( lume.all({2, 4, 6, 8}, function(x) return x % 2 == 0 end), true ) testeq( lume.all({2, 4, 6, 8}, function(x) return x % 2 == 0 end), true )
testeq( lume.all({{ x = 1 }, {}, { x = 3 }}, "x"), false )
testeq( lume.all({{ x = 1 }, { x = 2 }, { x = 3 }}, "x"), true )
testeq( lume.all({{ x = 1 }, { x = 2 }, { x = 3 }}, { x = 2 }), false )
testeq( lume.all({{ x = 2 }, { x = 2 }, { x = 2 }}, { x = 2 }), true )
end end
-- lume.any -- lume.any
@@ -157,6 +175,9 @@ tests["lume.any"] = function()
testeq( lume.any({false, false, false}), false ) testeq( lume.any({false, false, false}), false )
testeq( lume.any({2, 3, 4, 5}, function(x) return x % 2 == 0 end), true ) testeq( lume.any({2, 3, 4, 5}, function(x) return x % 2 == 0 end), true )
testeq( lume.any({1, 3, 5, 7}, function(x) return x % 2 == 0 end), false ) testeq( lume.any({1, 3, 5, 7}, function(x) return x % 2 == 0 end), false )
local t = {{ id = 10 }, { id = 20 }, { id = 30 }}
testeq( lume.any(t, { id = 10 }), true )
testeq( lume.any(t, { id = 40 }), false )
end end
-- lume.reduce -- lume.reduce
@@ -189,12 +210,23 @@ tests["lume.filter"] = function()
testeq( t, {2, 4} ) testeq( t, {2, 4} )
local t = lume.filter({a=1, b=2, c=3}, function(x) return x == 2 end, true) local t = lume.filter({a=1, b=2, c=3}, function(x) return x == 2 end, true)
testeq( t, {b=2} ) testeq( t, {b=2} )
local t = lume.filter({{ x=1, y=1 }, { x=2, y=2 }, { x=1, y=3 }}, { x = 1 })
testeq( t, {{ x=1, y=1 }, {x=1, y=3}} )
end end
-- lume.merge -- lume.merge
tests["lume.merge"] = function() tests["lume.merge"] = function()
testeq( lume.merge({1, 2, 3}, {8, 9, 0}), {1, 2, 3, 8, 9, 0} ) testeq( lume.merge(), {} )
testeq( lume.merge({a=1, b=2}, {b=3, c=4}, true), {a=1, b=3, c=4} ) testeq( lume.merge({x=1, y=2}), {x=1, y=2} )
testeq( lume.merge({a=1, b=2}, {b=3, c=4}), {a=1, b=3, c=4} )
end
-- lume.concat
tests["lume.concat"] = function()
testeq( lume.concat(nil), {} )
testeq( lume.concat({1, 2, 3}), {1, 2, 3} )
testeq( lume.concat({1, 2, 3}, {4, 5, 6}), {1, 2, 3, 4, 5, 6} )
testeq( lume.concat({1, 2, 3}, {4, 5, 6}, nil, {7}), {1, 2, 3, 4, 5, 6, 7} )
end end
-- lume.find -- lume.find
@@ -204,6 +236,40 @@ tests["lume.find"] = function()
testeq( lume.find({a=1, b=5, c=7}, 5), "b" ) testeq( lume.find({a=1, b=5, c=7}, 5), "b" )
end end
-- lume.match
tests["lume.match"] = function()
local t = { "a", "b", "c", "d" }
local t2 = { a = 1, b = 2, c = 3, d = 4 }
local t3 = { {x=1, y=2}, {x=3, y=4}, {x=5, y=6} }
local v, k = lume.match(t, function(x) return x > "c" end)
testeq( v, "d" )
testeq( k, 4 )
local v, k = lume.match(t, function(x) return x < "b" end)
testeq( v, "a" )
testeq( k, 1 )
local v, k = lume.match(t2, function(x) return x < 2 end)
testeq( v, 1 )
testeq( k, "a" )
local v, k = lume.match(t2, function(x) return x > 5 end)
testeq( v, nil )
testeq( k, nil )
local v, k = lume.match(t3, { x = 3, y = 4 })
testeq( k, 2 )
end
-- lume.count
tests["lume.count"] = function()
local t = { a = 1, b = 2, c = 5, [13] = 22, z = 8 }
testeq( lume.count(t), 5 )
testeq( lume.count(t, function(x) return x % 2 == 0 end ), 3 )
local a = { 5, 6, 7, 8, 9 }
testeq( lume.count(a), #a )
local t = { { n = 20 }, { n = 30 }, { n = 40 }, { n = 20 } }
testeq( lume.count(t, { n = 20 }), 2 )
testeq( lume.count(t, { n = 30 }), 1 )
testeq( lume.count(t, { n = 50 }), 0 )
end
-- lume.slice -- lume.slice
tests["lume.slice"] = function() tests["lume.slice"] = function()
testeq( lume.slice({"a", "b", "c", "d", "e"}, 2, 4), {"b", "c", "d"} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 2, 4), {"b", "c", "d"} )
@@ -218,16 +284,43 @@ tests["lume.slice"] = function()
testeq( lume.slice({"a", "b", "c", "d", "e"}, 0, 1), {"a"} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 0, 1), {"a"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, 0, 0), {} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 0, 0), {} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, -3), {"c", "d", "e"} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, -3), {"c", "d", "e"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, -3, 900), {"c", "d", "e"} )
end
-- lume.first
tests["lume.first"] = function()
local t = { "a", "b", "c", "d", "e" }
testeq( lume.first(t), "a" )
testeq( lume.first(t, 1), { "a" } )
testeq( lume.first(t, 2), { "a", "b" } )
end
-- lume.last
tests["lume.last"] = function()
local t = { "a", "b", "c", "d", "e" }
testeq( lume.last(t), "e" )
testeq( lume.last(t, 1), { "e" } )
testeq( lume.last(t, 2), { "d", "e" } )
end end
-- lume.invert -- lume.invert
tests["lume_invert"] = function() tests["lume.invert"] = function()
testeq( lume.invert({}), {} ) testeq( lume.invert({}), {} )
testeq( lume.invert{a = "x", b = "y"}, {x = "a", y = "b"} ) testeq( lume.invert{a = "x", b = "y"}, {x = "a", y = "b"} )
testeq( lume.invert{a = 1, b = 2}, {"a", "b"} ) testeq( lume.invert{a = 1, b = 2}, {"a", "b"} )
testeq( lume.invert(lume.invert{a = 1, b = 2}), {a = 1, b = 2} ) testeq( lume.invert(lume.invert{a = 1, b = 2}), {a = 1, b = 2} )
end end
-- lume.keys
tests["lume.keys"] = function()
testeq( lume.keys({}), {} )
local t = lume.keys({ aaa = 1, bbb = 2, ccc = 3 })
table.sort(t)
testeq( t, {"aaa", "bbb", "ccc"} )
local t = lume.keys({ "x", "x", "x" })
testeq( t, {1, 2, 3} )
end
-- lume.clone -- lume.clone
tests["lume.clone"] = function() tests["lume.clone"] = function()
local t = {6, 7, 4, 5} local t = {6, 7, 4, 5}
@@ -240,6 +333,7 @@ end
tests["lume.fn"] = function() tests["lume.fn"] = function()
local f = lume.fn(function(a, b) return a + b end, 10) local f = lume.fn(function(a, b) return a + b end, 10)
testeq( f(5), 15 ) testeq( f(5), 15 )
tester.test.error( lume.fn, 123 )
end end
-- lume.once -- lume.once
@@ -247,6 +341,43 @@ tests["lume.once"] = function()
local f = lume.once(function(a, b) return a + b end, 10) local f = lume.once(function(a, b) return a + b end, 10)
testeq( f(5), 15 ) testeq( f(5), 15 )
testeq( f(5), nil ) testeq( f(5), nil )
tester.test.error( lume.once, 123 )
end
-- lume.memoize
tests["lume.memoize"] = function()
local f = lume.memoize(
function(a, b, c)
return tostring(a) .. tostring(b) .. tostring(c)
end)
testeq( f("hello", nil, 15), "hellonil15" )
testeq( f("hello", nil, 15), "hellonil15" )
testeq( f(), "nilnilnil" )
testeq( f(), "nilnilnil" )
local f2 = lume.memoize(function() end)
testeq( f2(), nil )
end
-- lume.combine
tests["lume.combine"] = function()
local acc = 0
local a = function(x, y) acc = acc + x + y end
local b = function(x, y) acc = acc + x * y end
local fn = lume.combine(a, b)
fn(10, 20)
testeq( acc, 230 )
acc = 0
fn = lume.combine(nil, a, nil, b, nil)
fn(10, 20)
testeq( acc, 230 )
end
-- lume.call
tests["lume.call"] = function()
local add = function(a, b) return a + b end
testeq( lume.call(), nil )
testeq( lume.call(nil, 1, 2, 3), nil )
testeq( lume.call(add, 1, 2), 3 )
end end
-- lume.time -- lume.time
@@ -300,6 +431,16 @@ tests["lume.trim"] = function()
testeq( lume.trim("^.hello world]^", "^.]"), "hello world" ) testeq( lume.trim("^.hello world]^", "^.]"), "hello world" )
end end
-- lume.wordwrap
tests["lume.wordwrap"] = function()
local str = "A small string with some words and then some more words"
local b = "A small string with\nsome words and then\nsome more words"
local fn = function(str) return #str >= 20 end
testeq( lume.wordwrap(str), str )
testeq( lume.wordwrap(str, 20), b )
testeq( lume.wordwrap(str, fn), b )
end
-- lume.format -- lume.format
tests["lume.format"] = function() tests["lume.format"] = function()
local str = lume.format("a {a} in a {b}", {a = "mouse", b = "house"}) local str = lume.format("a {a} in a {b}", {a = "mouse", b = "house"})
@@ -319,11 +460,11 @@ tests["lume.trace"] = function()
print = function(x) print = function(x)
file, line, msg = x:match("%[(.-):(.-)%] (.*)") file, line, msg = x:match("%[(.-):(.-)%] (.*)")
end end
lume.trace("Hi world") lume.trace("Hi world", 123.456, 1, nil)
print = oldprint print = oldprint
testeq( file:match(".lua$"), ".lua" ) testeq( file:match(".lua$"), ".lua" )
testeq( tonumber(line) ~= nil, true ) testeq( tonumber(line) ~= nil, true )
testeq( msg, "Hi world" ) testeq( msg, "Hi world 123.46 1 nil" )
end end
-- lume.dostring -- lume.dostring
@@ -332,6 +473,12 @@ tests["lume.dostring"] = function()
testeq( lume.dostring([[return 12345]]), 12345 ) testeq( lume.dostring([[return 12345]]), 12345 )
end end
-- lume.uuid
tests["lume.uuid"] = function()
testeq( type(lume.uuid()), "string" )
testeq( #lume.uuid(), 36 )
end
-- lume.hotswap -- lume.hotswap
tests["lume.hotswap"] = function() tests["lume.hotswap"] = function()
local ok, err = lume.hotswap("bad_module_name") local ok, err = lume.hotswap("bad_module_name")
@@ -339,6 +486,18 @@ tests["lume.hotswap"] = function()
testeq( type(err), "string" ) testeq( type(err), "string" )
end end
-- lume.ripairs
tests["lume.ripairs"] = function()
local t = { "a", "b", "c" }
local r = {}
for i, v in lume.ripairs(t) do
table.insert(r, { i, v })
end
testeq( r, { { 3, "c" }, { 2, "b" }, { 1, "a" } })
for i, v in lume.ripairs(nil) do
end
end
-- lume.rgba -- lume.rgba
tests["lume.rgba"] = function() tests["lume.rgba"] = function()
local r, g, b, a = lume.rgba(0x12345678) local r, g, b, a = lume.rgba(0x12345678)
@@ -348,6 +507,15 @@ tests["lume.rgba"] = function()
testeq( b, 0x78 ) testeq( b, 0x78 )
end end
-- lume.chain
tests["lume.chain"] = function()
local t = lume.chain({1, 2}):map(function(x) return x * 2 end):result()
testeq( t, { 2, 4 } )
testeq( lume.chain(10):result(), 10 )
local t = lume({1, 2}):map(function(x) return x * 2 end):result()
testeq( t, { 2, 4 } )
end
tester.dotests(tests) tester.dotests(tests)
tester.test.global() tester.test.global()