Functional.hpp
Go to the documentation of this file.
1/*
2 Copyright 2016 Statoil ASA.
3
4 This file is part of the Open Porous Media project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#ifndef OPM_FUNCTIONAL_HPP
21#define OPM_FUNCTIONAL_HPP
22
23#include <algorithm>
24#include <iterator>
25#include <vector>
26#include <numeric>
27
28namespace Opm {
29
30namespace fun {
31
32 /*
33 * The Utility/Functional library provides convenient high level
34 * functionality and higher order functions inspiried by functional
35 * languages (in particular Haskell) and modern C++. The goal is to provide
36 * lightweight features that reduce boilerplate and make code more
37 * declarative.
38 */
39
40 /*
41 * map :: (a -> b) -> [a] -> [b]
42 *
43 * maps the elements [a] of the passed container C to [b], by using the
44 * passed function f :: a -> b. Works like map in haskell, lisp, python etc.
45 *
46 * C can be any foreach-compatible container (that supports .begin,
47 * .end), but will always return a vector.
48 *
49 * F can be any Callable, that is both function pointer,
50 * operator()-providing class or std::function, including lambdas. F is
51 * typically passed by reference. F must be unary of type A (which must
52 * match what C::const_iterator::operator* returns) and have return
53 * type B (by value).
54 *
55 * In short, this function deal with vector allocation, resizing and
56 * population based on some function f.
57 *
58 * fun::map( f, vec ) is equivalent to:
59 * vector dst;
60 * for( auto& x : vec ) dst.push_back( f( x ) );
61 * return dst;
62 *
63 * The behaviour is undefined if F has any side effects.
64 *
65 * --
66 *
67 * int plus1( int x ) { return x + 1; }
68 * base_vec = { 0, 1, 2, 3, 4 };
69 * vec = fun::map( &plus1, base_vec );
70 *
71 * vec => { 1, 2, 3, 4, 5 }
72 *
73 * --
74 *
75 * int mul2 = []( int x ) { return x * 2; };
76 * base_vec = { 0, 1, 2, 3, 4 };
77 * vec = fun::map( mul2, base_vec );
78 *
79 * vec => { 0, 2, 4, 6, 8 };
80 *
81 */
82 template< typename F, typename C >
83 std::vector< typename std::result_of< F( typename C::const_iterator::value_type& ) >::type >
84 map( F f, const C& src ) {
85 using A = typename C::const_iterator::value_type;
86 using B = typename std::result_of< F( A& ) >::type;
87 std::vector< B > ret;
88 ret.reserve( src.size() );
89
90 std::transform( src.begin(), src.end(), std::back_inserter( ret ), f );
91 return ret;
92 }
93
94 /*
95 * concat :: [[a]] -> [a]
96 *
97 * A primitive concat taking a vector of vectors, flattened into a
98 * single 1 dimensional vector. Moves all the elements so no unecessary
99 * copies are done.
100 *
101 * vec = { { 1 }, { 2, 2 }, { 3, 3, 3 } }
102 * cvec = concat( vec ) => { 1, 2, 2, 3, 3, 3 }
103 */
104 template< typename A >
105 std::vector< A > concat( std::vector< std::vector< A > >&& src ) {
106 const auto size = std::accumulate( src.begin(), src.end(), 0,
107 []( std::size_t acc, const std::vector< A >& x ) {
108 return acc + x.size();
109 }
110 );
111
112 std::vector< A > dst;
113 dst.reserve( size );
114
115 for( auto& x : src )
116 std::move( x.begin(), x.end(), std::back_inserter( dst ) );
117
118 return dst;
119 }
120
121
122 /*
123 * iota :: int -> [int]
124 * iota :: (int,int) -> [int]
125 *
126 * iota (ι) is borrowed from the APL programming language. This particular
127 * implementation behaves as a generator-like constant-space consecutive
128 * sequence of integers [m,n). Written to feel similar to std::iota, but as
129 * a producer instead of straight-up writer. This is similar to python2.7s
130 * xrange(), python3s range() and haskell's [0..(n-1)]. Some examples
131 * follow.
132 *
133 * Notes:
134 * * iota defaults to [0,n)
135 * * iota uses 0 indexing to feel more familiar to C++'s zero indexing.
136 * * iota can start at negative indices, but will always count upwards.
137 * * iota::const_iterator does not support operator-- (which would allow
138 * support for reverse iterators). This can be implemented if need arises.
139 * * iota is meant to play nice with the rest of fun and to be able to
140 * replace mundane for loops when the loops only purpose is to create the
141 * sequence of elements. iota can feel more declarative and work better
142 * with functions.
143 * * iota adds value semantics to things that in C++ normally relies on
144 * variable mutations. iota is meant to make it less painful to write
145 * immutable and declarative code.
146 * * as with all iterators, iota( n, m ) behaviour is undefined if m < n
147 * * unlike python's range, iota doesn't support steps (only increments).
148 * this is by design to keep this simple and minimal, as well as the name
149 * iota being somewhat unsuitable for stepping ranges. If the need for
150 * this arises it will be a separate function.
151 *
152 * fun::iota( 5 ) => [ 0, 1, 2, 3, 4 ]
153 * fun::iota( 3 ) => [ 0, 1, 2 ]
154 * fun::iota( 1, 6 ) => [ 1, 2, 3, 4, 5 ]
155 *
156 * --
157 *
158 * std::vector< int > vec ( 5, 0 );
159 * std::iota( vec.begin(), vec.end(), 0 );
160 * vec => [ 0, 1, 2, 3, 4 ]
161 *
162 * fun::iota i( 5 );
163 * std::vector vec( i.begin(), i.end() );
164 * vec => [ 0, 1, 2, 3, 4 ]
165 *
166 * --
167 *
168 * int plus( int x ) { return x + 1; }
169 * auto vec = fun::map( &plus, fun::iota( 5 ) );
170 * vec => [ 1, 2, 3, 4, 5 ]
171 *
172 * is equivalent to
173 *
174 * int plus( int x ) { return x + 1; }
175 * std::vector< int > vec;
176 * for( int i = 0; i < 5; ++i )
177 * vec.push_back( plus( i ) );
178 * vec => [ 1, 2, 3, 4, 5 ]
179 *
180 * --
181 *
182 * While not the primary intended use case, this enables foreach loop
183 * syntax over intervals:
184 *
185 * for( auto i : fun::iota( 5 ) )
186 * std::cout << i << " ";
187 *
188 * => 0 1 2 3 4
189 *
190 * for( auto i : fun::iota( 1, 6 ) )
191 * std::cout << i << " ";
192 *
193 * => 1 2 3 4 5
194 *
195 */
196 class iota {
197 public:
198 explicit iota( int end );
199 iota( int begin, int end );
200
202 public:
203 using difference_type = int;
204 using value_type = int;
205 using pointer = int*;
206 using reference = int&;
207 using iterator_category = std::forward_iterator_tag;
208
209 const_iterator() = default;
210
211 int operator*() const;
212
215
216 bool operator==( const const_iterator& rhs ) const;
217 bool operator!=( const const_iterator& rhs ) const;
218
219 private:
220 explicit const_iterator( int );
221 int value;
222
223 friend class iota;
224 };
225
226 size_t size() const;
227
230
231 private:
232 int first;
233 int last;
234 };
235
236}
237}
238
239#endif //OPM_FUNCTIONAL_HPP
Definition: Functional.hpp:201
int * pointer
Definition: Functional.hpp:205
std::forward_iterator_tag iterator_category
Definition: Functional.hpp:207
int value_type
Definition: Functional.hpp:204
bool operator==(const const_iterator &rhs) const
const_iterator & operator++()
bool operator!=(const const_iterator &rhs) const
const_iterator operator++(int)
int difference_type
Definition: Functional.hpp:203
int & reference
Definition: Functional.hpp:206
Definition: Functional.hpp:196
iota(int begin, int end)
const_iterator end() const
size_t size() const
const_iterator begin() const
std::vector< A > concat(std::vector< std::vector< A > > &&src)
Definition: Functional.hpp:105
std::vector< typename std::result_of< F(typename C::const_iterator::value_type &) >::type > map(F f, const C &src)
Definition: Functional.hpp:84
Definition: A.hpp:4
x y t t *t x y t t t x y t t t x *y t *t t x *y t *t t x y t t t x y t t t x(y+z)