83 Commits

Author SHA1 Message Date
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
rxi
89931c1ad8 Version 1.2.1 2014-03-27 17:57:19 +00:00
rxi
9ff4637201 Moved shared code of lume.split/trim() to local func 2014-03-19 21:05:34 +00:00
rxi
617729e261 Added more tests for lume.trim() 2014-03-19 20:18:32 +00:00
rxi
ad64d7af05 Fixed use of pattern special chars in lume.trim()
Using lua pattern special chars in the `chars` argument of lume.trim()
are now handled correctly and treated like any other character.
2014-03-19 20:14:48 +00:00
rxi
d69f419f5a Improved lume.split()'s README.md section for clarity 2014-03-19 19:49:50 +00:00
rxi
ad34f6ce33 Updated lume.split() in README.md 2014-03-19 13:02:55 +00:00
rxi
5882ca1303 Fixed minor error in lume.set()'s README.md example 2014-03-19 12:54:14 +00:00
rxi
828d23e6f6 Added new tests and updated some others for lume.split() 2014-03-19 12:53:01 +00:00
rxi
13a8edb2e7 Changed lume.split() to mimic python's str.split()
The lume.split() function now mimics python's str.split() function,
though it does not support the third argument (maxsplit)
2014-03-19 12:51:35 +00:00
rxi
425a52b99f Fixed spelling mistake in README.md 2014-03-18 18:01:12 +00:00
rxi
a234e36975 Version 1.2.0 2014-03-18 12:12:38 +00:00
rxi
4ba373c2d1 Localised global funcs/variables
Set most of the global funcs / variables which are used to local
variables. This offers better performance in lua 5.1 and 5.2, and seems
to make no difference in luaJIT.
2014-03-18 12:07:53 +00:00
rxi
c537d00a47 Added type tests for lume.random() 2014-03-18 12:04:36 +00:00
rxi
2c3be23a2b Added test for lume.lambda() caching 2014-03-15 21:58:35 +00:00
rxi
be40cd6276 Added caching to lume.lambda() 2014-03-15 21:58:26 +00:00
rxi
c971cee753 Added more tests for lume.slice() 2014-03-13 21:53:07 +00:00
rxi
151b57adc6 Fixed behaviour for negative i argument on lume.slice()
A negative value for the `i` argument of lume.slice() is now handled
properly (used as an index from the end of the string) to mimicing the
behaviour of lua's string.sub()
2014-03-13 21:32:22 +00:00
rxi
cbafc49e8a Added "unsupported type" error to lume.serialize()
The unsupported type error is raised if an unsupported type is
encountered in the table we are trying to serialize.
2014-03-12 21:16:32 +00:00
rxi
fcb1fa90d4 Reworded description of lume.weightedchoice() in README 2014-03-12 13:16:36 +00:00
rxi
2a6d1ea9a8 Added some tests for lume.weightedchoice() 2014-03-12 13:12:55 +00:00
rxi
2d7fa98155 Added lume.weightedchoice() 2014-03-12 13:12:45 +00:00
rxi
af4c919be4 Added tests for lume.lambda() 2014-03-09 00:11:06 +00:00
rxi
abdfd78354 Added tester.test.error() to tester.lua script 2014-03-08 23:00:47 +00:00
rxi
fbb5cbac1a Replaced table concat with str concat in lume.lambda()
This offers an improvement in performance for the function on Lua5.1,
5.2 and JIT 2.0.2
2014-03-08 22:19:55 +00:00
rxi
f8cbfc0bb0 Added lume.lambda() to README.md 2014-03-08 21:18:05 +00:00
rxi
cf031025fd Added lume.lambda() 2014-03-08 21:08:56 +00:00
rxi
4db5a6b683 Added another test for lume.reduce() 2014-03-08 16:19:31 +00:00
rxi
a4fe134985 Version 1.1.2 2014-03-08 16:10:16 +00:00
rxi
cbff46bdfb Fixed empty-table test for lume.invert() 2014-03-08 16:08:58 +00:00
rxi
5e15a57f37 Moved lume.invert()'s test func to match order in lume.lua 2014-03-08 15:47:28 +00:00
rxi
2f388bc2c7 Moved lume.invert() to be near lume.clone()
This makes the series of python-like functions (map, all, any, reduce,
set, filter) consecutive in the source and readme, which seems like a
more logical order.
2014-03-08 15:23:13 +00:00
rxi
8f4d1b158f Added empty-table test for lume.set() 2014-03-08 14:48:04 +00:00
rxi
1bbb795a89 Changed lume.set() to make use of lume.invert() 2014-03-08 14:47:27 +00:00
rxi
2c71079ef3 Added tests for lume.invert() 2014-03-08 14:45:18 +00:00
rxi
8a76fd7595 Added lume.invert() 2014-03-08 14:45:08 +00:00
rxi
b4bea5f4e0 Added new tests for lume.reduce() 2014-03-06 19:41:14 +00:00
rxi
51189d190d Made first argument in lume.reduce() optional 2014-03-06 19:33:13 +00:00
rxi
e6d47627cd Increased version to 1.1.1 2014-03-05 21:52:28 +00:00
rxi
05828bd840 Fixed module's table not updating in lume.hotswap()
Changed the returned values from pcall() in lume.hotswap() to be handled
correctly -- the second return value being the wrapped-function's first
return value. This bug would effect hotswapped modules which used the
modern approach of returning a table rather than creating globals.
2014-03-05 19:11:14 +00:00
rxi
83a051aadb Improved lume.clamp()'s performance on non-JIT
Removed use of the functions math.max() and math.min() and replaced them
with and/ors:

[5000000 calls, Lua5.2]
old func: 1.37 seconds
new func: 0.53 seconds
2014-03-05 12:44:19 +00:00
rxi
2699094218 Added better tests for lume.distance() 2014-03-05 12:22:05 +00:00
rxi
a08436445d Improved lume.distance()'s performance on non-JIT
Replaced use of the exponent operator with multiplications, yielding a
performance increase on non-JIT Lua:

[5000000 calls, Lua 5.1]
old func: 2.03 seconds
new func: 1.17 seconds

[5000000 calls, Lua 5.2]
old func: 1.60 seconds
new func: 0.89 seconds

[2000000000 calls, LuaJIT 2.0.2]
old func: 0.89 seconds
new func: 0.89 seconds
2014-03-05 12:18:49 +00:00
4 changed files with 444 additions and 68 deletions

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)
@@ -64,6 +64,16 @@ raised.
lume.randomchoice({true, false}) -- Returns either true or false lume.randomchoice({true, false}) -- Returns either true or false
``` ```
### lume.weightedchoice(t)
Takes the argument table `t` where the keys are the possible choices and the
value is the choice's weight. A weight should be 0 or above, the larger the
number the higher the probability of that choice being picked. If the table is
empty, a weight is below zero or all the weights are 0 then an error is raised.
```lua
lume.weightedchoice({ ["cat"] = 10, ["dog"] = 5, ["frog"] = 0 })
-- Returns either "cat" or "dog" with "cat" being twice as likely to be chosen.
```
### lume.shuffle(t) ### lume.shuffle(t)
Shuffles the values of array `t` in place, returns the array. Shuffles the values of array `t` in place, returns the array.
@@ -105,12 +115,14 @@ supplied it is called on each value, true is returned if any of the calls to
lume.any({1, 2, 1}, function(x) return x == 1 end) -- Returns true lume.any({1, 2, 1}, function(x) return x == 1 end) -- Returns true
``` ```
### lume.reduce(t, fn, first) ### lume.reduce(t, fn [, first])
Applies `fn` on two arguments cumulative to the items of the array `t`, from Applies `fn` on two arguments cumulative to the items of the array `t`, from
left to right, so as to reduce the array to a single value. The accumulator is left to right, so as to reduce the array to a single value. If a `first` value
intialised to the `first` value. is specified the accumulator is initialised to this, otherwise the first value
in the array is used. If the array is empty and no `first` value is specified
an error is raised,
```lua ```lua
lume.reduce({1, 2, 3}, function(a, b) return a + b end, 0) -- Returns 6 lume.reduce({1, 2, 3}, function(a, b) return a + b end) -- Returns 6
``` ```
### lume.set(t [, retainkeys]) ### lume.set(t [, retainkeys])
@@ -118,7 +130,7 @@ Returns a copy of the `t` table with all the duplicate values removed. If
`retainkeys` is true the table is not treated as an array and retains its `retainkeys` is true the table is not treated as an array and retains its
original keys. 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"}
``` ```
### lume.filter(t, fn [, retainkeys]) ### lume.filter(t, fn [, retainkeys])
@@ -144,6 +156,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.
@@ -151,6 +178,13 @@ 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.invert(t)
Returns a copy of the table where the keys have become the values and the
values the keys.
```lua
lume.invert({a = "x", b = "y"}) -- returns {x = "a", y = "b"}
```
### lume.clone(t) ### lume.clone(t)
Returns a shallow copy of the table `t`. Returns a shallow copy of the table `t`.
@@ -174,6 +208,25 @@ 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.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.
@@ -181,6 +234,15 @@ seconds the function `fn` took to execute followed by `fn`'s returned values.
lume.time(function(x) return x end, "hello") -- Returns 0, "hello" lume.time(function(x) return x end, "hello") -- Returns 0, "hello"
``` ```
### lume.lambda(str)
Takes a string lambda and returns a function. `str` should be a list of
comma-separated parameters, followed by `->`, followed by the expression which
will be evaluated and returned.
```lua
local f = lume.lambda "x,y -> 2*x+y"
f(10, 5) -- Returns 25
```
### lume.serialize(x) ### lume.serialize(x)
Serializes the argument `x` into a string which can be loaded again using Serializes the argument `x` into a string which can be loaded again using
`lume.deserialize()`. Only booleans, numbers, tables and strings can be `lume.deserialize()`. Only booleans, numbers, tables and strings can be
@@ -199,11 +261,12 @@ lume.deserialize("{1, 2, 3}") -- Returns {1, 2, 3}
``` ```
### lume.split(str [, sep]) ### lume.split(str [, sep])
Splits the string `str` into words and returns a table of the sub strings. If Returns an array of the words in the string `str`. If `sep` is provided it is
`sep` is provided the string will be split at any of the characters in `sep` used as the delimiter, consecutive delimiters are not grouped together and will
instead of on whitespace. delimit empty strings.
```lua ```lua
lume.split("One two three") -- Returns {"One", "two", "three"} lume.split("One two three") -- Returns {"One", "two", "three"}
lume.split("a,b,,c", ",") -- Returns {"a", "b", "", "c"}
``` ```
### lume.trim(str [, chars]) ### lume.trim(str [, chars])
@@ -237,6 +300,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
@@ -256,6 +323,17 @@ 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 }
```
## License ## License

227
lume.lua
View File

@@ -7,17 +7,44 @@
-- 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.1.0" } local lume = { _version = "1.4.1" }
local pairs, ipairs = pairs, ipairs
local type, assert, unpack = type, assert, unpack or table.unpack
local tostring, tonumber = tostring, tonumber
local math_floor = math.floor
local math_ceil = math.ceil
local math_random = math.random
local math_cos = math.cos
local math_atan2 = math.atan2
local math_sqrt = math.sqrt
local math_abs = math.abs
local math_pi = math.pi
local patternescape = function(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
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
function lume.clamp(x, min, max) function lume.clamp(x, min, max)
return math.max(math.min(x, max), min) return x < min and min or (x > max and max or x)
end end
function lume.round(x, increment) function lume.round(x, increment)
if increment then return lume.round(x / increment) * increment end if increment then return lume.round(x / increment) * increment end
return x > 0 and math.floor(x + .5) or math.ceil(x - .5) return x > 0 and math_floor(x + .5) or math_ceil(x - .5)
end end
@@ -32,42 +59,60 @@ 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
function lume.pingpong(x) function lume.pingpong(x)
return 1 - math.abs(1 - x % 2) return 1 - math_abs(1 - x % 2)
end end
function lume.distance(x1, y1, x2, y2, squared) function lume.distance(x1, y1, x2, y2, squared)
local s = (x1 - x2) ^ 2 + (y1 - y2) ^ 2 local dx = x1 - x2
return squared and s or math.sqrt(s) local dy = y1 - y2
local s = dx * dx + dy * dy
return squared and s or math_sqrt(s)
end end
function lume.angle(x1, y1, x2, y2) function lume.angle(x1, y1, x2, y2)
return math.atan2(y2 - y1, x2 - x1) return math_atan2(y2 - y1, x2 - x1)
end end
function lume.random(a, b) function lume.random(a, b)
if not a then a, b = 0, 1 end if not a then a, b = 0, 1 end
if not b then b = 0 end if not b then b = 0 end
return a + math.random() * (b - a) return a + math_random() * (b - a)
end end
function lume.randomchoice(t) function lume.randomchoice(t)
return t[math.random(#t)] return t[math_random(#t)]
end
function lume.weightedchoice(t)
local sum = 0
for k, v in pairs(t) do
assert(v >= 0, "weight value less than zero")
sum = sum + v
end
assert(sum ~= 0, "all weights are zero")
local rnd = lume.random(sum)
for k, v in pairs(t) do
if rnd < v then return k end
rnd = rnd - v
end
end end
function lume.shuffle(t) function lume.shuffle(t)
for i = 1, #t do for i = 1, #t do
local r = math.random(#t) local r = math_random(#t)
t[i], t[r] = t[r], t[i] t[i], t[r] = t[r], t[i]
end end
return t return t
@@ -76,7 +121,7 @@ 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
@@ -117,16 +162,18 @@ end
function lume.reduce(t, fn, first) function lume.reduce(t, fn, first)
for i = 1, #t do first = fn(first, t[i]) end local acc = first or t[1]
return first assert(acc, "reduce of an empty array with no first value")
for i = first and 1 or 2, #t do acc = fn(acc, t[i]) end
return acc
end end
function lume.set(t, retainkeys) function lume.set(t, retainkeys)
local tmp = {}
for k, v in pairs(t) do tmp[v] = k end
local rtn = {} local rtn = {}
for k, v in pairs(tmp) do rtn[retainkeys and v or (#rtn + 1)] = k end for k, v in pairs(lume.invert(t)) do
rtn[retainkeys and v or (#rtn + 1)] = k
end
return rtn return rtn
end end
@@ -156,13 +203,41 @@ function lume.find(t, value)
end end
function lume.slice(t, i, j) function lume.match(t, fn)
i = i or 1 for k, v in pairs(t) do
j = j and (j < 0 and (#t + j + 1) or j) or #t if fn(v) then return v, k end
local rtn = {}
for i = math.max(i, 1), math.min(j, #t) do
rtn[#rtn + 1] = t[i]
end end
return nil
end
function lume.count(t, fn)
local count = 0
if fn then
for k, v in pairs(t) do
if fn(v) then count = count + 1 end
end
else
for k in pairs(t) do count = count + 1 end
end
return count
end
function lume.slice(t, i, j)
i = i and absindex(#t, i) or 1
j = j and absindex(#t, j) or #t
local rtn = {}
for x = i < 1 and 1 or i, j > #t and #t or j do
rtn[#rtn + 1] = t[x]
end
return rtn
end
function lume.invert(t)
local rtn = {}
for k, v in pairs(t) do rtn[v] = k end
return rtn return rtn
end end
@@ -175,6 +250,7 @@ end
function lume.fn(fn, ...) function lume.fn(fn, ...)
assert(iscallable(fn), "expected a function as the first argument")
local args = {...} local args = {...}
return function(...) return function(...)
local a = lume.merge(lume.clone(args), {...}) local a = lume.merge(lume.clone(args), {...})
@@ -194,6 +270,39 @@ 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.time(fn, ...) function lume.time(fn, ...)
local start = os.clock() local start = os.clock()
local rtn = {fn(...)} local rtn = {fn(...)}
@@ -201,6 +310,19 @@ function lume.time(fn, ...)
end end
local lambda_cache = {}
function lume.lambda(str)
if not lambda_cache[str] then
local args, body = str:match([[^([%w,_ ]-)%->(.-)$]])
assert(args and body, "bad string lambda")
local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend"
lambda_cache[str] = lume.dostring(s)
end
return lambda_cache[str]
end
function lume.serialize(x) function lume.serialize(x)
local f = { string = function(v) return string.format("%q", v) end, local f = { string = function(v) return string.format("%q", v) end,
number = tostring, boolean = tostring } number = tostring, boolean = tostring }
@@ -211,6 +333,8 @@ function lume.serialize(x)
end end
return "{" .. table.concat(rtn) .. "}" return "{" .. table.concat(rtn) .. "}"
end end
local err = function(t,k) error("unsupported serialize type: " .. k) end
setmetatable(f, { __index = err })
return f[type(x)](x) return f[type(x)](x)
end end
@@ -221,12 +345,19 @@ end
function lume.split(str, sep) function lume.split(str, sep)
return lume.array(str:gmatch("([^" .. (sep or "%s") .. "]+)")) if not sep then
return lume.array(str:gmatch("([%S]+)"))
else
assert(sep ~= "", "empty separator")
local psep = patternescape(sep)
return lume.array((str..sep):gmatch("(.-)("..psep..")"))
end
end end
function lume.trim(str, chars) function lume.trim(str, chars)
chars = chars or "%s" if not chars then return str:match("^[%s]*(.-)[%s]*$") end
chars = patternescape(chars)
return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$") return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$")
end end
@@ -242,8 +373,13 @@ 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, ...)
x = (type(x) == "number") and lume.round(x, .01) or (x or "nil")
t[#t + 1] = tostring(x)
end
print(table.concat(t, " "))
end end
@@ -252,6 +388,16 @@ 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 = {}
@@ -264,12 +410,13 @@ function lume.hotswap(modname)
if type(v) == "table" then update(old[k], v) else old[k] = v end if type(v) == "table" then update(old[k], v) else old[k] = v end
end end
end end
local oldmod = pcall(require, modname) or nil
local err = nil local err = nil
local function onerror(e) local function onerror(e)
for k, v in pairs(_G) do _G[k] = oldglobal[k] end for k, v in pairs(_G) do _G[k] = oldglobal[k] end
err = lume.trim(e) err = lume.trim(e)
end end
local ok, oldmod = pcall(require, modname)
oldmod = ok and oldmod or nil
xpcall(function() xpcall(function()
package.loaded[modname] = nil package.loaded[modname] = nil
local newmod = require(modname) local newmod = require(modname)
@@ -288,13 +435,27 @@ end
function lume.rgba(color) function lume.rgba(color)
local floor = math.floor local a = math_floor((color / 16777216) % 256)
local a = floor((color / 2 ^ 24) % 256) local r = math_floor((color / 65536) % 256)
local r = floor((color / 2 ^ 16) % 256) local g = math_floor((color / 256) % 256)
local g = floor((color / 2 ^ 08) % 256) local b = math_floor((color) % 256)
local b = 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
return lume return lume

View File

@@ -64,11 +64,11 @@ end
-- lume.distance -- lume.distance
tests["lume.distance"] = function() tests["lume.distance"] = function()
testeq( lume.distance(10, 20, 10, 20), 0 ) testeq( lume.distance(15, 20, 15, 20), 0 )
testeq( lume.distance(10, 20, 20, 20), 10 ) testeq( lume.distance(13, 44, 156, 232), 236.205419074 )
local x = lume.distance(1, 2, 5, 7) testeq( lume.distance(-23, 66, -232, 123), 216.633330769 )
testeq( lume.distance(1, 2, 5, 7, true), x * x ) local x = lume.distance(13, 15, -2, 81)
testeq( lume.distance(10, 10, 10, 20, true), 10 * 10 ) testeq( lume.distance(13, 15, -2, 81, true), x * x )
end end
-- lume.angle -- lume.angle
@@ -82,6 +82,9 @@ end
-- lume.random -- lume.random
tests["lume.random"] = function() tests["lume.random"] = function()
testeq( type(lume.random()), "number" )
testeq( type(lume.random(1)), "number" )
testeq( type(lume.random(1, 2)), "number" )
end end
-- lume.randomchoice -- lume.randomchoice
@@ -94,6 +97,15 @@ tests["lume.randomchoice"] = function()
testeq( lume.randomchoice({true}), true ) testeq( lume.randomchoice({true}), true )
end end
-- lume.weightedchoice
tests["lume.weightedchoice"] = function()
testeq( lume.weightedchoice( {a = 1} ), "a" )
testeq( lume.weightedchoice( {a = 0, b = 1} ), "b" )
tester.test.error( lume.weightedchoice, {} )
tester.test.error( lume.weightedchoice, { a = 0, b = 0 } )
tester.test.error( lume.weightedchoice, { a = 1, b = -1 } )
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}
@@ -103,7 +115,6 @@ tests["lume.shuffle"] = function()
testeq( lume.shuffle({}), {} ) testeq( lume.shuffle({}), {} )
end 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}))
@@ -151,12 +162,19 @@ end
-- lume.reduce -- lume.reduce
tests["lume.reduce"] = function() tests["lume.reduce"] = function()
local concat = function(a, b) return a .. b end local concat = function(a, b) return a .. b end
local add = function(a, b) return a + b end
testeq( lume.reduce({"cat", "dog"}, concat, ""), "catdog" ) testeq( lume.reduce({"cat", "dog"}, concat, ""), "catdog" )
testeq( lume.reduce({"cat", "dog"}, concat, "pig"), "pigcatdog" ) testeq( lume.reduce({"cat", "dog"}, concat, "pig"), "pigcatdog" )
testeq( lume.reduce({"me", "ow"}, concat), "meow" )
testeq( lume.reduce({1, 2, 3, 4}, add), 10 )
testeq( lume.reduce({1, 2, 3, 4}, add, 5), 15 )
testeq( lume.reduce({1}, add), 1 )
testeq( lume.reduce({}, concat, "potato"), "potato" )
end end
-- lume.set -- lume.set
tests["lume.set"] = function() tests["lume.set"] = function()
testeq( lume.set({}), {} )
local t = lume.set({1, 2, 3, 2, 5, 6, 6}) local t = lume.set({1, 2, 3, 2, 5, 6, 6})
table.sort(t) table.sort(t)
testeq( t, {1, 2, 3, 5, 6} ) testeq( t, {1, 2, 3, 5, 6} )
@@ -186,15 +204,56 @@ 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 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 )
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 )
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"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, 2, -2), {"b", "c", "d"} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 2, -2), {"b", "c", "d"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, 3, -1), {"c", "d", "e"} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 3, -1), {"c", "d", "e"} )
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"}, 4), {"d", "e"} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 4), {"d", "e"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, 1, 1), {"a"} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 1, 1), {"a"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, 2, 1), {} ) testeq( lume.slice({"a", "b", "c", "d", "e"}, 2, 1), {} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, -3, -2), {"c", "d"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, -3, 1), {} )
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"}, -3), {"c", "d", "e"} )
testeq( lume.slice({"a", "b", "c", "d", "e"}, -3, 900), {"c", "d", "e"} )
end
-- lume.invert
tests["lume_invert"] = function()
testeq( lume.invert({}), {} )
testeq( lume.invert{a = "x", b = "y"}, {x = "a", y = "b"} )
testeq( lume.invert{a = 1, b = 2}, {"a", "b"} )
testeq( lume.invert(lume.invert{a = 1, b = 2}), {a = 1, b = 2} )
end end
-- lume.clone -- lume.clone
@@ -209,6 +268,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
@@ -216,6 +276,35 @@ 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 end
-- lume.time -- lume.time
@@ -225,6 +314,21 @@ tests["lume.time"] = function()
testeq( {a, b, c}, {50, 60, 70} ) testeq( {a, b, c}, {50, 60, 70} )
end end
-- lume.lambda
tests["lume.lambda"] = function()
testeq( lume.lambda "x->x*x"(10), 100 )
testeq( lume.lambda "x->x*x"(20), 400 )
testeq( lume.lambda "x,y -> 2*x+y"(10,5), 25 )
testeq( lume.lambda "a, b -> a / b"(1, 2), .5 )
testeq( lume.lambda "a -> 'hi->' .. a"("doggy"), "hi->doggy" )
testeq( lume.lambda "A1,_->A1.._"("te","st"), "test" )
testeq( lume.lambda "->"(1,2,3), nil )
tester.test.error( lume.lambda, "abc" )
tester.test.error( lume.lambda, "" )
tester.test.error( lume.lambda, "a,b->a->b" )
tester.test.error( lume.lambda, "(a),b->a+b" )
end
-- lume.serialize / lume.deserialize -- lume.serialize / lume.deserialize
tests["lume.serialize, lume.deserialize"] = function() tests["lume.serialize, lume.deserialize"] = function()
local t = { 1, 2, 3, 4, true, false, "cat", "dog", {1, 2, 3} } local t = { 1, 2, 3, 4, true, false, "cat", "dog", {1, 2, 3} }
@@ -234,9 +338,15 @@ end
-- lume.split -- lume.split
tests["lume.split"] = function() tests["lume.split"] = function()
testeq( lume.split("cat dog pig"), {"cat", "dog", "pig"} ) testeq( lume.split("cat dog pig"), {"cat", "dog", "pig"} )
testeq( lume.split(",cat,dog,pig", ","), {"cat", "dog", "pig"} ) testeq( lume.split("cat,dog,pig", ","), {"cat", "dog", "pig"} )
testeq( lume.split(",cat,dog;pig", ",;"), {"cat", "dog", "pig"} ) testeq( lume.split("cat,dog;pig", ";"), {"cat,dog", "pig"} )
testeq( lume.split("cat,dog,,pig", ","), {"cat", "dog", "", "pig"} )
testeq( lume.split(";;;cat;", ";"), {"", "", "", "cat", ""} )
testeq( lume.split("cat.dog", "."), {"cat", "dog"} )
testeq( lume.split("cat%dog", "%"), {"cat", "dog"} )
testeq( lume.split("1<>2<>3", "<>"), {"1", "2", "3"} )
tester.test.error( lume.split, "abc", "" )
end end
-- lume.trim -- lume.trim
@@ -244,6 +354,8 @@ tests["lume.trim"] = function()
testeq( lume.trim(" hello world "), "hello world" ) testeq( lume.trim(" hello world "), "hello world" )
testeq( lume.trim("-=-hello-world===", "-="), "hello-world" ) testeq( lume.trim("-=-hello-world===", "-="), "hello-world" )
testeq( lume.trim("***hello world*-*", "*"), "hello world*-" ) testeq( lume.trim("***hello world*-*", "*"), "hello world*-" )
testeq( lume.trim("...hello world.", "."), "hello world" )
testeq( lume.trim("^.hello world]^", "^.]"), "hello world" )
end end
-- lume.format -- lume.format
@@ -265,11 +377,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
@@ -278,6 +390,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")
@@ -294,6 +412,13 @@ 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 )
end
tester.dotests(tests) tester.dotests(tests)
tester.test.global() tester.test.global()

View File

@@ -128,6 +128,18 @@ function tester.test.equal(result, expected)
end end
function tester.test.error(fn, ...)
local passed = not pcall(fn, ...)
local info = debug.getinfo(2)
if passed then
dopass(info.short_src, info.currentline)
else
dofail(info.short_src, info.currentline)
printfailmsg("Expected an error to be raised")
end
end
function tester.dotests(t) function tester.dotests(t)
local keys = {} local keys = {}
for k in pairs(t) do table.insert(keys, k) end for k in pairs(t) do table.insert(keys, k) end