Giter Site home page Giter Site logo

lume's Introduction

Lume

A collection of functions for Lua, geared towards game development.

Installation

The lume.lua file should be dropped into an existing project and required by it:

lume = require "lume"

Function Reference

lume.clamp(x, min, max)

Returns the number x clamped between the numbers min and max

lume.round(x [, increment])

Rounds x to the nearest integer; rounds away from zero if we're midway between two integers. If increment is set then the number is rounded to the nearest increment.

lume.round(2.3) -- Returns 2
lume.round(123.4567, .1) -- Returns 123.5

lume.sign(x)

Returns 1 if x is 0 or above, returns -1 when x is negative.

lume.lerp(a, b, amount)

Returns the linearly interpolated number between a and b, amount should be in the range of 0 - 1; if amount is outside of this range it is clamped.

lume.lerp(100, 200, .5) -- Returns 150

lume.smooth(a, b, amount)

Similar to lume.lerp() but uses cubic interpolation instead of linear interpolation.

lume.pingpong(x)

Ping-pongs the number x between 0 and 1.

lume.distance(x1, y1, x2, y2 [, squared])

Returns the distance between the two points. If squared is true then the squared distance is returned -- this is faster to calculate and can still be used when comparing distances.

lume.angle(x1, y1, x2, y2)

Returns the angle between the two points.

lume.vector(angle, magnitude)

Given an angle and magnitude, returns a vector.

local x, y = lume.vector(0, 10) -- Returns 10, 0

lume.random([a [, b]])

Returns a random number between a and b. If only a is supplied a number between 0 and a is returned. If no arguments are supplied a random number between 0 and 1 is returned.

lume.randomchoice(t)

Returns a random value from array t. If the array is empty an error is raised.

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.

lume.weightedchoice({ ["cat"] = 10, ["dog"] = 5, ["frog"] = 0 })
-- Returns either "cat" or "dog" with "cat" being twice as likely to be chosen.

lume.isarray(x)

Returns true if x is an array -- the value is assumed to be an array if it is a table which contains a value at the index 1. This function is used internally and can be overridden if you wish to use a different method to detect arrays.

lume.push(t, ...)

Pushes all the given values to the end of the table t and returns the pushed values. Nil values are ignored.

local t = { 1, 2, 3 }
lume.push(t, 4, 5) -- `t` becomes { 1, 2, 3, 4, 5 }

lume.remove(t, x)

Removes the first instance of the value x if it exists in the table t. Returns x.

local t = { 1, 2, 3 }
lume.remove(t, 2) -- `t` becomes { 1, 3 }

lume.clear(t)

Nils all the values in the table t, this renders the table empty. Returns t.

local t = { 1, 2, 3 }
lume.clear(t) -- `t` becomes {}

lume.extend(t, ...)

Copies all the fields from the source tables to the table t and returns t. If a key exists in multiple tables the right-most table's value is used.

local t = { a = 1, b = 2 }
lume.extend(t, { b = 4, c = 6 }) -- `t` becomes { a = 1, b = 4, c = 6 }

lume.shuffle(t)

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.

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(...)

Iterates the supplied iterator and returns an array filled with the values.

lume.array(string.gmatch("Hello world", "%a+")) -- Returns {"Hello", "world"}

lume.each(t, fn, ...)

Iterates the table t and calls the function fn on each value followed by the supplied additional arguments; if fn is a string the method of that name is called for each value. The function returns t unmodified.

lume.each({1, 2, 3}, print) -- Prints "1", "2", "3" on separate lines
lume.each({a, b, c}, "move", 10, 20) -- Does x:move(10, 20) on each value

lume.map(t, fn)

Applies the function fn to each value in table t and returns a new table with the resulting values.

lume.map({1, 2, 3}, function(x) return x * 2 end) -- Returns {2, 4, 6}

lume.all(t [, fn])

Returns true if all the values in t table are true. If a fn function is supplied it is called on each value, true is returned if all of the calls to fn return true.

lume.all({1, 2, 1}, function(x) return x == 1 end) -- Returns false

lume.any(t [, fn])

Returns true if any of the values in t table are true. If a fn function is supplied it is called on each value, true is returned if any of the calls to fn return true.

lume.any({1, 2, 1}, function(x) return x == 1 end) -- Returns true

lume.reduce(t, fn [, first])

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. If a 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.

lume.reduce({1, 2, 3}, function(a, b) return a + b end) -- Returns 6

lume.unique(t)

Returns a copy of the t array with all the duplicate values removed.

lume.unique({2, 1, 2, "cat", "cat"}) -- Returns {1, 2, "cat"}

lume.filter(t, fn [, retainkeys])

Calls fn on each value of t table. Returns a new table with only the values where fn returned true. If retainkeys is true the table is not treated as an array and retains its original keys.

lume.filter({1, 2, 3, 4}, function(x) return x % 2 == 0 end) -- Returns {2, 4}

lume.reject(t, fn [, retainkeys])

The opposite of lume.filter(): Calls fn on each value of t table; returns a new table with only the values where fn returned false. If retainkeys is true the table is not treated as an array and retains its original keys.

lume.reject({1, 2, 3, 4}, function(x) return x % 2 == 0 end) -- Returns {1, 3}

lume.merge(...)

Returns a new table with all the given tables merged together. If a key exists in multiple tables the right-most table's value is used.

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.

lume.concat({1, 2}, {3, 4}, {5, 6}) -- Returns {1, 2, 3, 4, 5, 6}

lume.find(t, value)

Returns the index/key of value in t. Returns nil if that value does not exist in the table.

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.

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.

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]])

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.

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.

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.

lume.last({"a", "b", "c"}) -- Returns "c"

lume.invert(t)

Returns a copy of the table where the keys have become the values and the values the keys.

lume.invert({a = "x", b = "y"}) -- returns {x = "a", y = "b"}

lume.pick(t, ...)

Returns a copy of the table filtered to only contain values for the given keys.

lume.pick({ a = 1, b = 2, c = 3 }, "a", "c") -- Returns { a = 1, c = 3 }

lume.keys(t)

Returns an array containing each key of the table.

lume.clone(t)

Returns a shallow copy of the table t.

lume.fn(fn, ...)

Creates a wrapper function around function fn, automatically inserting the arguments into fn which will persist every time the wrapper is called. Any arguments which are passed to the returned function will be inserted after the already existing arguments passed to fn.

local f = lume.fn(print, "Hello")
f("world") -- Prints "Hello world"

lume.once(fn, ...)

Returns a wrapper function to fn which takes the supplied arguments. The wrapper function will call fn on the first call and do nothing on any subsequent calls.

local f = lume.once(print, "Hello")
f() -- Prints "Hello"
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.

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.

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.

lume.call(print, "Hello world") -- Prints "Hello world"

lume.time(fn, ...)

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.

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.

local f = lume.lambda "x,y -> 2*x+y"
f(10, 5) -- Returns 25

lume.serialize(x)

Serializes the argument x into a string which can be loaded again using lume.deserialize(). Only booleans, numbers, tables and strings can be serialized. Circular references will result in an error; all nested tables are serialized as unique tables.

lume.serialize({a = "test", b = {1, 2, 3}, false})
-- Returns "{[1]=false,["a"]="test",["b"]={[1]=1,[2]=2,[3]=3,},}"

lume.deserialize(str)

Deserializes a string created by lume.serialize() and returns the resulting value. This function should not be run on an untrusted string.

lume.deserialize("{1, 2, 3}") -- Returns {1, 2, 3}

lume.split(str [, sep])

Returns an array of the words in the string str. If sep is provided it is used as the delimiter, consecutive delimiters are not grouped together and will delimit empty strings.

lume.split("One two three") -- Returns {"One", "two", "three"}
lume.split("a,b,,c", ",") -- Returns {"a", "b", "", "c"}

lume.trim(str [, chars])

Trims the whitespace from the start and end of the string str and returns the new string. If a chars value is set the characters in chars are trimmed instead of whitespace.

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.

-- Returns "Hello world\nThis is a\nshort string"
lume.wordwrap("Hello world. This is a short string", 14)

lume.format(str [, vars])

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 can also be used.

lume.format("{b} hi {a}", {a = "mark", b = "Oh"}) -- Returns "Oh hi mark"
lume.format("Hello {1}!", {"world"}) -- Returns "Hello world!"

lume.trace(...)

Prints the current filename and line number followed by each argument separated by a space.

-- Assuming the file is called "example.lua" and the next line is 12:
lume.trace("hello", 1234) -- Prints "example.lua:12: hello 1234"

lume.dostring(str)

Executes the lua code inside str.

lume.dostring("print('Hello!')") -- Prints "Hello!"

lume.uuid()

Generates a random UUID string; version 4 as specified in RFC 4122.

lume.hotswap(modname)

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 be the same string used when loading the module with require(). In the case of an error the global environment is restored and nil plus an error message is returned.

lume.hotswap("lume") -- Reloads the lume module
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.

-- 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.color(str [, mul])

Takes color string str and returns 4 values, one for each color channel (r, g, b and a). By default the returned values are between 0 and 1; the values are multiplied by the number mul if it is provided.

lume.color("#ff0000")               -- Returns 1, 0, 0, 1
lume.color("rgba(255, 0, 255, .5)") -- Returns 1, 0, 1, .5
lume.color("#00ffff", 256)          -- Returns 0, 256, 256, 256
lume.color("rgb(255, 0, 0)", 256)   -- Returns 256, 0, 0, 256

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.

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().

lume({1, 2, 3}):each(print) -- Prints 1, 2 then 3 on separate lines

Iteratee functions

Several lume functions allow a table, string or nil to be used in place of their iteratee function argument. The functions that provide this behaviour are: map(), all(), any(), filter(), reject(), match() and count().

If the argument is nil then each value will return itself.

lume.filter({ true, true, false, true }, nil) -- { true, true, true }

If the argument is a string then each value will be assumed to be a table, and will return the value of the key which matches the string.

local t = {{ z = "cat" }, { z = "dog" }, { z = "owl" }}
lume.map(t, "z") -- Returns { "cat", "dog", "owl" }

If the argument is a table then each value will return true or false, depending on whether the values at each of the table's keys match the collection's value's values.

local t = {
  { age = 10, type = "cat" },
  { age = 8,  type = "dog" },
  { age = 10, type = "owl" },
}
lume.count(t, { age = 10 }) -- returns 2

License

This library is free software; you can redistribute it and/or modify it under the terms of the MIT license. See LICENSE for details.

lume's People

Contributors

icrawler avatar jaythomas avatar rxi avatar technomancy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lume's Issues

lume.sign(x) with 0 -> 0 ?

Would it make sense to return 0 if x == 0?

In my use case I just spent 5 minutes looking for my mistake having assumed the function worked like this. But perhaps my use case is not the average use case.

pass keys to lume.map function

I'm currently in the process of swapping my home-brewed util library for lume. one sticking point is that my version of map passes the value and key to the mapper function, which means I have to modify lume a bit. it should be as simple as changing rtn[k] = f(v) to rtn[k] = f(v, k). I seriously doubt that this would break any existing code (aren't dynamic function arities wonderful?) so if folks think it's a good idea I can make a pull request for this, might also propagate this change to some of the other higher order functions like filter while I'm at it

setTimeout() and setInterval()

Hi! Would it be possible to implement setTimeout() and setInterval() functions à la Javascript, along with their respective clearing functions? They would be very useful in many cases.

I am not very good with lua myself, so I don't know how to implement these. I do hope that these functions fit the scope of this library though!

lume.format(str, table) doesn't properly handle false values

Simple test:

local t = lume.format("{1} is {2} is {3}", { true, false, true })
print(t) -- true is {2} is true

I guess this is of the way Lua deals with false values, not sure what a proper fix would be, but if I can figure one out I will send a PR.

Support writing functions/threads in lume.serialize

Right now in lume.serialize it signals an error if you give it a value that cannot be serialized. Usually this is the right choice, but there are times when you are debugging and want to use it to spit out some tables and you don't necessarily need it to be deserializable.

I would suggest that lume.serialize take an additional unreadable_ok flag which changes the behavior to simply emitting unserializable values using tostring instead of erroring out.

I have the code for this if you decide it's a good idea.

lume.wordwrap adds leading newline when first line is longer than limit

When the input to wordwrap starts with a word longer than the limit, it inserts a leading newline, but it doesn't make sense to add newline there.

Repro

print("|".. lume.wordwrap("supercalifragilisticexpialidocious", 20) .."|")
-- expected:
|supercalifragilisticexpialidocious|

-- actual:
|
supercalifragilisticexpialidocious|

Using lume.wordwrap at 98847e7.

Workaround

Easy to workaround by adding lume.trim().

hotswap discards non-table values

https://github.com/rxi/lume/blob/da0d1bb/lume.lua#L694-L695

    local newmod = require(modname)
    if type(oldmod) == "table" then update(oldmod, newmod) end

currently, if the returned value of a module isn't a table, it (newmod) is discarded.

in my case, my module returns a function, and i expect package.loaded to contain the new value. i am using lurker.lua to trigger hotswapping.

local some_state = 'blah'
local function exec_stuff(name)
  local f = require('stuff_'..name) -- bug: never returns new function value
  some_state = f(some_state)
end

this dirty hack seems to work:

-    if type(oldmod) == "table" then update(oldmod, newmod) end
+    if type(oldmod) == "table" then update(oldmod, newmod) else oldmod = newmod end

lume.reduce with a falsy initial value

Presently, if you provide lume.reduce a falsy value for the initial accumulator, the function will act as though nothing has been provided. It looks like this is because on lume.lua:290, there's an implicit falsy check, rather than an explicit nil check. For reference: https://github.com/rxi/lume/blob/master/lume.lua#L290.

This could be solved with the following:

local started = (first ~= nil) and true or false

I'm not so familiar with Lua, so this may add in unexpected behavior, but I thought I'd report it all the same.

EDIT: I'm more than happy to make the change if you want me to, but it seems small enough that it would add a bunch of overhead for me to fork + PR + review + merge.

Possibility of adding lume.deepclone

Hey there,

Not sure if you’re still working on this repo at all or what, but I figured I would give it a shot. I’ve been using lume on a small game I’ve been working on, very handy little library, big fan so far.

I ran into a case where I needed to make a simple clone of a table, and the lume.clone out of the box only does sub tables by reference, which was a bit of an issue in the code I was working on.

After doing a little bit of research I found a couple of very sane ways of doing a bit more thorough cloning. If you are up for having a deepclone, or even just replacing the current lume.clone, I would be happy to shoot you a pull request.

Let me know what you think, and thanks again for the excellent library!

luacheck?

Would you be interested in a patch to let Lume check out clean under luacheck? It's mostly just changing unused loop variables to _ and renaming a few shadowing variables.

`lume.isarray` doesn't return correct results for mixed tables or empty arrays

{} is considered not an array, as ({})[1] == nil.

{1, a = 1, b = 2} is considered an array as the first element is not nil. This will make getiter loop over it using ipairs, skipping the hash part containing a and b keys.

I'm not sure how this should be fixed because checking the hash part usually involves iterating over it using pairs, which has a linear time as it might first visit the entire list part. The current check may be wrong in these "edge cases", but at least runs in constant time.

unable to install via luarocks with lua 5.4

Will not install for Lua 5.4:

$ luarocks --local install lume

Error: No results matching query were found for Lua 5.4.
To check if it is available for other Lua versions, use --check-lua-versions.

Sort functions alphabetically

A passerby thought: list functions alphabetically in README.md, lume.lua and test/test.lua; and force the order for future development. Binary search is faster than the brute one. 🙂

val^0.5 faster than math.sqrt(val)?

One less function call...

Example:

function lume.distance(x1, y1, x2, y2, squared)
  local dx = x1 - x2
  local dy = y1 - y2
  local s = dx * dx + dy * dy
  return squared and s or s^0.5
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.