Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/seanchas116/amulet
C++ library for container manipulation and monads
https://github.com/seanchas116/amulet
Last synced: 4 days ago
JSON representation
C++ library for container manipulation and monads
- Host: GitHub
- URL: https://github.com/seanchas116/amulet
- Owner: seanchas116
- License: mit
- Created: 2013-09-16T08:05:47.000Z (about 11 years ago)
- Default Branch: master
- Last Pushed: 2013-12-06T02:34:40.000Z (almost 11 years ago)
- Last Synced: 2024-04-14T03:13:55.029Z (7 months ago)
- Language: C++
- Homepage:
- Size: 754 KB
- Stars: 14
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Amulet
======Amulet is a header-only C++11 library that provides some functional programming support (mainly for containers and monads) like in Underscore.js, Ruby, Scala or LINQ.
- Add convenient functional methods to containers (`Amulet::RangeExtension`)
- Lazy (deferred) evaluation of returned containers
- Comprehension syntax (query macro)
- Optional values (`Amulet::Option`)## RangeExtension
`Amulet::RangeExtension` is a template class that inherits the given container class (which must have `begin()` and `end()` methods)
and adds a set of convenient methods for container manipulation to the original class.All methods provided by RangeExtension is immutable (const) and most of the returned containers are lazily evaluated.
### Some Examples
```cpp
#includeRangeExtension> xs = {1,2,3};
// produce a new container by transforming each value by a function
xs.map([](int x){
return x * 2;
}); // => {2,4,6}// collect values that fulfill a condition
xs.filter([](int x){
return x % 2 == 0;
}); // => {2}// make a reverse container
xs.reverse(); // => {3,2,1}// sum up the values
xs.foldLeft(0, [](int sum, int x){
return sum + x;
}); // => 6// map and flatten
xs.flatMap([](int x){
return RangeExtension>{x, x};
}); // => {1,1,2,2,3,3}// append index to each value
xs.withIndex(); // => {{0,1},{1,2},{2,3}}
```For detail, see the reference (at the bottom of this page).
### Extend existing containers
`Amulet::extend` wraps a normal container and make RangeExtension methods available.
```cpp
#includestd::vector vec = {1,2,3};
auto twices = Amulet.extend(vec).map([](int x){
return x * 2;
});
```### Int Range
`Amulet::intRange` returns an integer range that has RangeExtension methods.
```cpp
#includeauto fizzbuzz = Amulet::intRange(0, 100).map([](int x)->std::string{
if (x % 15 == 0)
return "fizzbuzz";
else if (x % 3 == 0)
return "fizz";
else if (x % 5 == 0)
return "buzz";
else
return std::to_string(x);
});std::copy(fizzbuzz.begin(), fizzbuzz.end(), std::ostream_iterator(std::cout, " "));
std::cout << std::endl;
```## Query Macro (Comprehension)
Amulet provides a comprehension syntax similar to LINQ query expressions, for-comrehension in Scala or do-notation in Haskell, using preprocessor macro.
**NOTE: On clang, add `#define BOOST_PP_VARIADICS 1` on top of your source code to use this feature.**
This enables variadic support of Boost.Preprocessor (on clang it is disabled by default but Amulet needs it).```cpp
#include
#includetemplate
using ExVector = Amulet::RangeExtension>;auto xs = ExVector{1,2};
auto ys = ExVector{3,4};auto product = _do(
_from(x, xs),
_from(y, ys),
_select(std::make_pair(x, y))
); // => {{1,3},{1,4},{2,3},{2,4}}ExVector> prices = {
{"orange", 50},
{"apple", 100},
{"banana", 150},
{"carrot",90}};// comprehension version
auto fruitPrices1 = _do(
_from(pair, prices),
_where(pair.first != "carrot"),
_select(pair.second)
);// method chaining version
auto fruitsPrices2 = prices.filter([](const std::pair &pair){
return pair.first != "carrot";
}).map([](const std::pair &pair){
return pair.second;
});// simpler method chaining version, using values() to collect the second value from each pair
auto fruitsPrices3 = prices.filter([](const std::pair &pair){
return pair.first != "carrot";
}).values();
```## Option
`Amulet::Option` is a container-like object that represents 'optional' values.
It internally uses `boost::optional`.While `boost::optional` is analogous to pointers, `Amulet::Option` is analogous to containers (containers that can have 0 or 1 value).
Since Option is analogous to containers, you can use query macro with Option values.
```cpp
#include
#includeauto divide = [](int x, int y) -> Amulet::Option{
if (y)
return Amulet::some(x / y); // succeeded
else
return Amulet::none; // failed, returns invalid value
};
auto a = Amulet::some(0);
auto b = Amulet::some(1);auto divided = _do(
_from(x, b),
_from(y, a),
divide(x, y)
); // has no value because divide() will be failedauto added1 = _do(
_from(x, a),
_from(y, b),
_select(x + y)
); // Amulet::Optional(1)auto added2 = _do(
_from(x, a),
_from(y, divided),
_select(x + y)
); // has no value because divided has no value
```## Monad
Monad is a sort of design pattern heavily used in Haskell and Scala.
In Amulet, Monad concept is defined as followings:
- has `value_type` type.
- has `template flatMap(F f)` method.
- has `template fromValue(const T &vaue)` static method.RangeExtension and Option fulfills this concept.
In fact, query macro can be used with any values which type fulfills Monad concept.
## Reference
### RangeExtension
- `each(function)`
Passes every value in the container to a function.
```cpp
RangeExtension> xs = {1,2,3};
xs.each([](int x){
std::cout << x << std::endl;
});
```- `eachPair(function)`
Passes every pair in the container to a function.
Available only if the value type of the container is std::pair or a equivalent one.```cpp
RangeExtension> xs = {{1, "one"},{2, "two"},{3, "three"}};
xs.eachPair([](int x, const std::string &str){
std::cout << x << ": " << str << std::endl;
});
```- `eachWithIndex(function)`
Passes every value and its index to a function.
Equivalent to `withIndex().eachPair````cpp
RangeExtension> xs = {1,2,3};
xs.eachWithIndex([](size_t index, int x){
std::cout << index << ": " << x << std::endl;
});
```- `foldLeft(initialState, function)`
Passes each value and the current state to a function in left-to-right order and sets the current state to the return value.
```
RangeExtension> xs = {1,2,3};
xs.foldLeft(0, [](int sum, int x){
return sum + x;
}); // => 6
```- `foldRight(initialState, function)`
Passes each value and the current state to a function in right-to-left order and sets the current state to the return value.
Equivalent to `reverse().foldLeft`- `min()`
Returns the minimum value.
- `max()`
Returns the maximum value.
- `find(predicate)`
Finds the first value that fulfill the condition.
Return type is `Option`.```cpp
RangeExtension> xs = {1,2,3};
xs.find([](int x){
return x % 2 == 0;
}); // Option(2)
xs.find([](int x){
return x < 0;
}); // Option() (has no value)
```- `contains(value)`
Returns whether the container contains the value.
```cpp
RangeExtension> xs = {1,2,3};
xs.contains(1); // => true
xs.contains(0); // => false
```- `zip(otherContainer)`
Merges together each value of the container and another container into a std::pair.
The size of merged container will be the minimum of the sizes of original ones.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,2,3};
RangeExtension> ys = {4,5,6,7};
xs.zip(ys); // => {{1,4},{2,5},{3,6}}
```- `filter(predicate)`
Collects all values that fulfill a condition.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,2,3};
xs.filter([](int x){
return x % 2 != 0;
}); // => {1,3}
```- `map(function)`
Produces a new container by transforming each value by a function.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,2,3};
xs.map([](int x){
return x * 2;
}); // => {2,4,6}
```- `flatten()`
Flattens a container of containers.
The result is lazily-evaluated.```cpp
RangeExtension>
>> xs = {{1,2,3},{4,5,6},{7,8,9}};
xs.flatten(); // => {1,2,3,4,5,6,7,8,9}
```- `flatMap(function)`
Equivalent to `map(function).flatten()`.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,2,3};
xs.flatMap([](int x){
return RangeExtension>{x,x};
}); // => {1,1,2,2,3,3}
```- `reverse()`
Makes a reverse container.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,2,3};
xs.reverse(); // => {3,2,1}
```- `withIndex()`
Merges together each index and each value int a std::pair.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,2,3};
xs.withIndex(); // => {{0,1},{1,2},{2,3}}
```- `unique()`
Removes any duplicates.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,2,3,1};
xs.unique(); // => {1,2,3}
```- `firsts(), keys()`
Collects the first value of each pair.
Available only if the value type of the container is std::pair or a equivalent one.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {{1, "one"},{2, "two"},{3, "three"}};
xs.firsts(); // => {1,2,3}
```- `seconds(), values()`
Collects the second value of each pair.
Available only if the value type of the container is std::pair or a equivalent one.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {{1, "one"},{2, "two"},{3, "three"}};
xs.seconds(); // => {"one","two","three"}
```- `sort(), stableSort()`
Sorts the values.
```cpp
RangeExtension> xs = {1,3,2,0};
xs.sort(); // => {0,1,2,3}
```- `sortBy(compare), stableSortBy(compare)`
Sorts the values by a compare function.
```cpp
RangeExtension> xs = {1,3,2,0};
xs.sortBy([](int x, int y){
return x > y;
}); // => {3,2,1,0}
```- `partial(beginIndex, endIndex)`
Returns a sliced container from `beginIndex` until `endIndex`.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,3,2,0};
xs.partial(1,3) // => {3,2}
```- `slice(firstIndex, lastIndex)`
Returns a sliced container from `firstIndex` to `lastIndex`.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,3,2,0};
xs.slice(1,3) // => {3,2,0}
```- `mid(firstIndex, size)`
Returns a sliced container from `firstIndex` until `firstIndex + size`.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,3,2,0};
xs.mid(1,2) // => {3,2}
```- `narrow(frontOffset, backOffset)`
Drops the first `frontOffset` values and the last `backOffset` values.
The result is lazily-evaluated.```cpp
RangeExtension> xs = {1,3,2,0};
xs.narrow(1,1) // => {3,2}
```- `head()`
Returns the first value.
- `tail()`
Drops the first value.
Equivalent to `narrow(1,0)`.- `init()`
Drops the last value.
Equivalent to `narrow(0,1)`.- `tail()`
Returns the last value.
- `to()`
Creates a new container and puts all the values into it.
```cpp
RangeExtension> xs = {1,3,2,0};
auto newVector = xs.map([](int x){
return x * 2;
}).to>(); // => std::vector{2,4,6}
```### Option