Giter Club home page Giter Club logo

tpl's Introduction

tpl: Template library for piping operations

Overview

Tpl is C++ header-only library designed to simplify basic collections/containers operations. It strongly relies on C++ templates allowing the compiler to do heavy-lifting and generate efficient code.

Using ideas from functional languages and some more modern imperative languages (like Kotlin) combined with power of C++ gives many benefits:

  • value semantics
  • type safety
  • speed
  • lazy evaluation - nothing is ever computed if you don't ask for result
  • syntax simplification via operator overloads
  • readability and elegance
  • pure functions that are easy to reason about

Build statuses

Linux gcc 4.9+

gcc 4.9 gcc 5 gcc 6 gcc 7 gcc 8
Build Status Build Status Build Status Build Status Build Status

Linux clang 3.9+

clang 3.9 clang 4 clang 5 clang 6 clang 7
Build Status Build Status Build Status Build Status Build Status

Visual Studio 2015+

Visual Studio 2015/2017
Build status

Getting started

Requirements

For library:

  • C++14 compliant compiler (compilers that work for sure are listed above)

Catch framework is shipped with the library so for building and running tests You need:

  • cmake tool

Installation

tpl is a header only library. The only things You have to do are:

  • clone this repository git clone https://github.com/bartop/tpl.git
  • (optionally) setup additional include path in Your project to tpl/include/

If you want to build the tests You have to do the following:

  • clone this repository git clone https://github.com/bartop/tpl.git
  • enter main directory of the repository cd tpl
  • make directory for build and enter it mkdir build && cd build
  • generate files for desired build system using cmake .. -G <name_of_cmake_generator>
  • (if using makefiles) to run all tests use ctest

Sample code

Filter collection

#include <tpl.hpp>
#include <iostream>

using namespace tpl;
using namespace std;

int main(){
  const auto collection = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  const auto filtered = collection | filter([](const auto &i){ return i > 5 });
  for(const auto i : filtered)
    cout << i;
  //output will be 6, 7, 8, 9, 10
  return 0;
}

Transform collection

#include <tpl.hpp>
#include <iostream>

using namespace tpl;
using namespace std;

int main(){
  const auto collection = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  const auto transformed = collection | transform([](const auto &i){ return i > 5; });//transform to booleans
  for(const auto i : transformed)
    cout << i;
  //output will be 0, 0, 0, 0, 0, 1, 1, 1, 1, 1
  return 0;
}

Pepare operations for further use

#include <tpl.hpp>
#include <iostream>

using namespace tpl;
using namespace std;

int main(){
  const auto collection1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  const auto collection2 = { 1, 11 };
  const auto operation =
      filter([](const auto &i){ return i > 5 }) | 
      transform([](const auto &i){ return i + 1; });
  const auto result1 = collection1 | operation;
  const auto result2 = collection2 | operation;
  for(const auto i : result1)
    cout << i; //output will be 7, 8, 9, 10, 11
    
  for(const auto i : result2)
    cout << i; //output will be 12
    
  return 0;
}

Extract keys of the map

#include <tpl.hpp>
#include <iostream>
#include <map>

using namespace tpl;
using namespace std;

int main(){
  const map<int, int> map = { {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10} };
  const auto keys = map | keys;
  for(const auto i : keys)
    cout << i;
  //output will be 1, 3, 5, 7, 9
  return 0;
}

Flatten the collection of collections

#include <tpl.hpp>
#include <iostream>
#include <vector>

using namespace tpl;
using namespace std;

int main(){
  const vector<vector<int>> vec = { {1, 2, 3, 4}, {5, 6}, {7, 8, 9} , {10} };
  const auto flat = vec | flatten;
  for(const auto i : flat)
    cout << i;
  //output will be 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  return 0;
}

Fold left the collection

#include <tpl.hpp>
#include <iostream>
#include <vector>

using namespace tpl;
using namespace std;

int main(){
  const auto collection = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  const auto fold = vec | fold_left([](const auto &l, const auto &r) { return l + r; });
  cout << i;
  //output will be 55
  return 0;
}

Generate 10 items from cyclic generator

#include <tpl.hpp>
#include <iostream>
#include <vector>

using namespace tpl;
using namespace std;

int main(){
  const auto collection = cycle(vector<int>{ 1, 2, 3 }) | take(10);//initializer list is not accepted, sadly
  for(const auto &i : collection)
    cout << i;
  //output will be 1, 2, 3, 1, 2, 3, 1, 2, 3, 1
  return 0;
}

More complex example

The whole power of this solutin is ease of extension. For example, if you want to filter, sort and transform you can do it just like that:

const auto afterComplexOperation = map
  | keys
  | filter([](const auto &i){ /* your filter here */ }
  | sort([](const auto &i, const auto &j){ /* your sort predicate */}
  | transform([](const auto &i){ /* apply your transformation */})
  | flatten;

tpl's People

Contributors

bartop avatar

Stargazers

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

Watchers

 avatar  avatar

tpl's Issues

Update README.md

Lot's of features has been added since last readme update. There is also a lot to clarify and fix.

Implement take operator

Operator take

Input

Enumerable and unsigned number.

Output

Enumerable containing given numer of elements from the beginning of the enumerable argument.

Example

const auto input1 = { 1, 2, 3, 4, 5 };
const auto out = intput1 | take(2);
//out should contain 1, 2

Implement infinite enumerable

Enumerable 'infinite'

Input

Any value with copy constructor

Output

Enumerable returning the input value at every iteration.

Example

for(const auto &i : infinite(3))
    cout << i << ", "; //should print 3, 3, 3, 3 .........

Implement keys operator

Operator keys

Input

Associable enumerable.

Output

Enumerable to iterate through keys of given associable enumerable.

Example

const auto input = { std::make_pair(1, 2), std::make_pair(3, 4), std::make_pair(5, 6) };
const auto output = input | keys;
for(const auto &i : output)
    std::cout << i;
//output should be 1, 3, 5

Implement mapped_values operator

Operator mapped_values

Input

Associable enumerable.

Output

Enumerable to iterate through mapped values of given associable enumerable.

Example

const auto input = { std::make_pair(1, 2), std::make_pair(3, 4), std::make_pair(5, 6) };
const auto output = input | keys;
for(const auto &i : output)
    std::cout << i;
//output should be 2, 4, 6

Refactor holders to factories

Currently many operations are using "holders" to construct operators later. There should be more OO solution used - instead of holders factories should be used with create method.

Add cache operator

Currently, every time iterator is dereferenced whole computation process is required. Cache operator would store the data at some point to avoid computing same data multiple times.

Implement copy_to operator

Operator copy_to

Input

Enumerable and output iterator to copy into.

Output

No output. The outcome is that all elements from enumerable are copied into the output iterator,

Example

const auto input = { 1, 2, 3, 4, 5, 6 };
std::vector<int> output; 
input | copy_to(std::back_inserter(output));
//vector output should contain 1, 2, 3, 4, 5, 6

Reduce copying of data even more

Currently some data (mostly in holders) is passed always by value. It is not very bad, but can be fixed using perfect forwarding.

Implement "recursive" version of existing operators

"Deep" versions of algorithms would work for nested enumerables without breaking this nested hierarchy. For example:

const auto input = { (std::vector{1, 2}), (std::vector{3, 4, 5}), (std::vector{6}) };
const auto output1 = input | deep_filter([](const auto &i){ return i >=2; });
// output1 is now {{2}, {3, 4, 5}, {6}}

const auto output2 = input | deep_filter([](const auto &i){ return i < 6; });
// output2 is now {{1, 2}, {3, 4, 5}, {}}

I will gladly disscuss pros and cons of keeping the empty containers inside hierachy.

Our TODO list is:

  • transformed
  • sorted
  • filtered
  • flattened

Implement fold_left operator

Operator fold_left

Input

Enumerable and folding functiion. Optionally folding start value
Enumerable, folding function and folding start value. If start value is not given, default value of the enumerable value type is used as initial.

Output

Folded value depending on folding function.

Additional info

As fold is not so easy to describe here is given quite good explanation:
https://en.wikipedia.org/wiki/Fold_(higher-order_function)

Example 1

const auto input = { 1, 2, 3, 4, 5, 6 };
//sum example
const auto output = input | fold_left((const auto &a, const auto &b)[]{ return a + b; });
//output should be 21

Example 2

const auto input = { 1, 2, 3, 4, 5, 6 };
//sum example
const auto output = input | fold_left((const auto &a, const auto &b)[]{ return a + b; }, 2/*fold initial value*/);
//output should be 23

Add grouping headers for all directories

There should be operator.hpp for all headers in operator directory, sink.hpp for all in sink directory and so forth. Additionaly there should be a tpl.hpp including all of these.

Add AppVeyor CI

Currently only Travis is supported - Windows builds would be helpful.

Make sorted_sequence not cache data

Currently sorted_sequence caches the input only once. It can be a problem when dealing with mutable input enumerables. All in all, it should sort data everytime it is requested iterator.

Add possibility to compose operations

Currently the whole "flow" of data from source through operators to sinks is required for operator | to work properly. It would be useful to be able to compose more complex operators using the ones provided without need to specify input and/or output.

Change folder structure of library

Root should be directory include. Inside should go tpl directory with its contents. Operation directory sholud be renamed to operator.

Implement reverse operator

Operator reverse

Input

Enumerable.

Output

Enumerable with reversed order of elements.

Example

const auto input = { 1, 2, 3, 4, 5, 6 };
//sum example
const auto output = input | reverse;
//output should contain { 6, 5, 4, 3, 2, 1 };

Remarks

This feature depends on implementation of:

  • #8 Implementation of reverse iterators for enumerables

Rename Container to Enumerable

For code to be more readable it should be used with internal naming - every Container in code should be raplaced with Enumerable.

Add gcc 4.9 in Travis CI

gcc4.9 has theoretically all the features this library needs to work. We should try to add it to the matrix.

Segregate the headers into subfolders

Currently there is quite a lot files in tpl directory. It should have few subdirectories like: sources, operators, sinks with corresponding headeres put inside them.

Implement flatten operator

Operator flatten

Input

Enumerable of enumerables of generic type T.

Output

Enumerable containing all elements of the internal enumerables from input.

Example

const auto input = { (std::vector{1, 2}), (std::vector{3, 4, 5}), (std::vector{6}) };
const auto output = input | flatten;
for(const auto &i : output)
    std::cout << i;
//output should be 1, 2, 3, 4, 5, 6

Add coverage control

Currently there is no code coverage control. Yet for the code to be tested well the coverage should be measured and controlled all the time.

Implement generator enumerable

Enumerable generator

Input

Single argument function returning type convertible to its argument and initializing value of the same type as the function.

Output

Enumerable returning value "generated" by the function. Generator passes previous generated value to the function which builds up new value from it and returns it.

Example

for(const auto &i : generator([](const auto &i){ return i+i; }, 1))
    cout << i << ", "; //should print 1, 2, 4, 8, 16, ......

Implement drop operator

Operator drop

Input

Enumerable and unsigned number.

Output

Enumerable from the argument without given numer of elements from beginning.

Example

const auto input1 = { 1, 2, 3, 4, 5 };
const auto out = intput1 | drop(2);
//out should contain 3, 4, 5

Implement zip operator

Operator zip

Input

Two enumerables

Output

Enumerable coresponding to "zipping" of two enumerables. For example from zip({1, 2}, {'a', 'b'}) -> { (1, 'a'), (2, 'b') }. In case zipped enumerables have different number of elements, the longer is truncated at the end.

Example

const auto input1 = { 1, 2, 3 };
const auto input2 = { 'a' , 'b', 'c', 'd' };
const auto zippped = zip(input1, input2);//zipped should contain { (1, 'a'), (2, 'b'), (3, 'c') }
//optionally input1 | zip(input2)

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.