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