LibFunctional-1.0 is a library that provides a set of functions that allow a more functional style of programming in lua, such as map, filter and bind. Its main purpose is to replace the most commonly used loops with calls to descriptive functions, therefore making the purpose of the operation clearer and increasing the code readability.
I'll be tagging a version soon, therefore locking the API, unless someone wants to request a change.
I don't like the name invert, does not feel intuitive.
The name is taken from underscore.js. In the context of this library (one that mainly works on lists and tables) I think it is good enough, but I am open to suggestions.
min and max could optionally take a comparator function.
Both of these functions are implemented on terms of reduce, for example min is a call to reduce(list, math.min), so the rationale there is that if you need something more specialized you should use reduce instead.
Some names are strange, for example reverse returnes a copy, while shuffle does so inplace. Maybe reversed would be a better name?
In the spirit of functional programming, all the functions should return copies. The exceptions are sort and shuffle, which work in-place by default for performance (but both have versions that return copies). Probably I should make sort and shuffle returns copies by default, and name the versions that work in-place something else ?
equal takes an argument for shallow or not, why doesn't clone do?
I am thinking that the best option to fix that would be making equal shallow by default. Additionally I am going to change clone to work with tables instead of only lists.
I will add some once the API is locked, it is a lot of work and I don't want to make changing things harder at this point. Meanwhile, if you don't mind terse code, there is a tests.lua file in the repository and in the packaged files that contains a few tests for all of the functions, and it may serve as usage examples as well.
The name is taken from underscore.js. In the context of this library (one that mainly works on lists and tables) I think it is good enough, but I am open to suggestions.
After thinking about a table as a map, invert totally makes sense. Would be interesting to know what the behaviour of non-injective tables is.
Consider:
local t = { a = "c", b = "c" }
I suppose the result is undefined? Might be worth mentioning.
Both of these functions are implemented on terms of reduce, for example min is a call to reduce(list, math.min), so the rationale there is that if you need something more specialized you should use reduce instead.
In the spirit of functional programming, all the functions should return copies. The exceptions are sort and shuffle, which work in-place by default for performance (but both have versions that return copies). Probably I should make sort and shuffle returns copies by default, and name the versions that work in-place something else ?
I agree that they should return copies. I was complaining about the inconsistent naming.
Maybe shuffle_inplace?
Also I am missing zipWith. While this can be done by
zipWith = function(f, a, b)
return map(zip(a, b), function(t) f(t[1], t[2]) end
end
this creates unecessary tables. Maybe a direct implementation would be more efficient?
contains, find_first_of, find_last_of, find_if create a new table, assign it to r, but never use nor return it.
find_first_of, find_last_of could be implemented by creating a map out of ... (with true as a value) instead of using contains. The result would be in o(n) instead of o(n²).
Moreover, this :
local len = #list
for i = 1, len do
local v = list[i]
could be written as the more idiomatic form :
for i, v in ipairs(list) do
Some functions could also apply to maps as way as lists by using "for k, v in pairs(list) do" in their loops.
And lastly, there is no need for :
local len = #list
for i = s, len do
for arguments are evaluated only once, so it is equivalent for this, which is more idiomatic :
First of all thanks Adirelle, I appreciate that you took the effort to review the code.
The reason that ipairs is not used it is because it is noticeably slower than the equivalent for loop, and the len local, while not used inside the loop in every function, it is used in some, and writing all the loops in the same form should make the code more uniform and easier to read, while having no performance cost.
As for not using pairs(), I have tried to make every function that would make sense to be used on a table to work on tables, but otherwise I believe that it is better to use the keys() and values() functions if you only want to work on the keys or values of a table, rather than silently ignoring the keys.
I believe find_first_of and first_last_of are o(n*m) at the moment (m being the number of different values to search for) rather than o(n²), so it shouldn't be _that_ bad, but yeah I agree that making a lookup map should be even faster, I'll run some tests and change it.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
I'll be tagging a version soon, therefore locking the API, unless someone wants to request a change.
Project page
API documentation
Also some usage examples might be nice.
The name is taken from underscore.js. In the context of this library (one that mainly works on lists and tables) I think it is good enough, but I am open to suggestions.
Both of these functions are implemented on terms of reduce, for example min is a call to reduce(list, math.min), so the rationale there is that if you need something more specialized you should use reduce instead.
In the spirit of functional programming, all the functions should return copies. The exceptions are sort and shuffle, which work in-place by default for performance (but both have versions that return copies). Probably I should make sort and shuffle returns copies by default, and name the versions that work in-place something else ?
It won't, I will make the documentation more clear about that.
I am thinking that the best option to fix that would be making equal shallow by default. Additionally I am going to change clone to work with tables instead of only lists.
I will add some once the API is locked, it is a lot of work and I don't want to make changing things harder at this point. Meanwhile, if you don't mind terse code, there is a tests.lua file in the repository and in the packaged files that contains a few tests for all of the functions, and it may serve as usage examples as well.
After thinking about a table as a map, invert totally makes sense. Would be interesting to know what the behaviour of non-injective tables is.
Consider:
I suppose the result is undefined? Might be worth mentioning.
That makes sense.
I agree that they should return copies. I was complaining about the inconsistent naming.
Maybe shuffle_inplace?
Also I am missing zipWith. While this can be done by
this creates unecessary tables. Maybe a direct implementation would be more efficient?
The best part is that it is not only usable on WoW.
Thanks
contains, find_first_of, find_last_of, find_if create a new table, assign it to r, but never use nor return it.
find_first_of, find_last_of could be implemented by creating a map out of ... (with true as a value) instead of using contains. The result would be in o(n) instead of o(n²).
Moreover, this :
could be written as the more idiomatic form :
Some functions could also apply to maps as way as lists by using "for k, v in pairs(list) do" in their loops.
And lastly, there is no need for :
for arguments are evaluated only once, so it is equivalent for this, which is more idiomatic :
The reason that ipairs is not used it is because it is noticeably slower than the equivalent for loop, and the len local, while not used inside the loop in every function, it is used in some, and writing all the loops in the same form should make the code more uniform and easier to read, while having no performance cost.
As for not using pairs(), I have tried to make every function that would make sense to be used on a table to work on tables, but otherwise I believe that it is better to use the keys() and values() functions if you only want to work on the keys or values of a table, rather than silently ignoring the keys.
I believe find_first_of and first_last_of are o(n*m) at the moment (m being the number of different values to search for) rather than o(n²), so it shouldn't be _that_ bad, but yeah I agree that making a lookup map should be even faster, I'll run some tests and change it.