├── .editorconfig ├── .gitignore ├── .gitmodules ├── LICENSE.txt ├── projects └── CMake │ └── CMakeLists.txt ├── readme.md ├── src └── ducer │ ├── ducer.h │ ├── ducer_arities.h │ ├── ducer_compose.h │ ├── ducer_filterer.h │ ├── ducer_into.h │ ├── ducer_mapper.h │ ├── ducer_pass.h │ ├── ducer_println.h │ ├── ducer_summer.h │ ├── ducer_taker.h │ └── ducer_transduce.h └── test ├── gist.cpp └── test.cpp /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Visual Studio 3 | ################# 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | 8 | # User-specific files 9 | *.suo 10 | *.user 11 | *.sln.docstates 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Rr]elease/ 16 | *_i.c 17 | *_p.c 18 | *.ilk 19 | *.meta 20 | *.obj 21 | *.pch 22 | *.pdb 23 | *.pgc 24 | *.pgd 25 | *.rsp 26 | *.sbr 27 | *.tlb 28 | *.tli 29 | *.tlh 30 | *.tmp 31 | *.vspscc 32 | .builds 33 | *.dotCover 34 | 35 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 36 | #packages/ 37 | 38 | # Visual C++ cache files 39 | ipch/ 40 | *.aps 41 | *.ncb 42 | *.opensdf 43 | *.sdf 44 | 45 | # Visual Studio profiler 46 | *.psess 47 | *.vsp 48 | 49 | # ReSharper is a .NET coding add-in 50 | _ReSharper* 51 | 52 | # Installshield output folder 53 | [Ee]xpress 54 | 55 | # DocProject is a documentation generator add-in 56 | DocProject/buildhelp/ 57 | DocProject/Help/*.HxT 58 | DocProject/Help/*.HxC 59 | DocProject/Help/*.hhc 60 | DocProject/Help/*.hhk 61 | DocProject/Help/*.hhp 62 | DocProject/Help/Html2 63 | DocProject/Help/html 64 | 65 | # Click-Once directory 66 | publish 67 | 68 | # Others 69 | [Bb]in 70 | [Oo]bj 71 | sql 72 | TestResults 73 | *.Cache 74 | ClientBin 75 | stylecop.* 76 | ~$* 77 | *.dbmdl 78 | Generated_Code #added for RIA/Silverlight projects 79 | 80 | # Backup & report files from converting an old project file to a newer 81 | # Visual Studio version. Backup files are not needed, because we have git ;-) 82 | _UpgradeReport_Files/ 83 | Backup*/ 84 | UpgradeLog*.XML 85 | 86 | 87 | 88 | ############ 89 | ## Windows 90 | ############ 91 | 92 | # Windows image file caches 93 | Thumbs.db 94 | 95 | # Folder config file 96 | Desktop.ini 97 | 98 | ############ 99 | ## Mac 100 | ############ 101 | 102 | .DS_Store 103 | 104 | ############ 105 | ## CMake 106 | ############ 107 | 108 | projects/* 109 | !projects/CMake/CMakeLists.txt 110 | !projects/nuget/rxcpp.autopackage 111 | 112 | ############ 113 | ## Sublime 114 | ############ 115 | 116 | *.sublime-* 117 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/catch"] 2 | path = ext/catch 3 | url = https://github.com/philsquared/Catch.git 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Kirk Shoop 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /projects/CMake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(ducer) 4 | 5 | MESSAGE( STATUS "CMAKE_CXX_COMPILER_ID: " ${CMAKE_CXX_COMPILER_ID} ) 6 | 7 | FIND_PACKAGE(Threads) 8 | 9 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 10 | list( APPEND CMAKE_CXX_FLAGS " -std=c++1y -ftemplate-depth=1024 ${CMAKE_CXX_FLAGS}") 11 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 12 | list( APPEND CMAKE_CXX_FLAGS " -std=c++1y ${CMAKE_CXX_FLAGS}") 13 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 14 | list( APPEND CMAKE_CXX_FLAGS " /DUNICODE /D_UNICODE /bigobj ${CMAKE_CXX_FLAGS}") 15 | endif() 16 | 17 | 18 | # define some folders 19 | get_filename_component(DUCER_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PATH) 20 | get_filename_component(DUCER_DIR "${DUCER_DIR}" PATH) 21 | 22 | MESSAGE( STATUS "DUCER_DIR: " ${DUCER_DIR} ) 23 | 24 | 25 | include_directories(${DUCER_DIR}/ext/catch/include ${DUCER_DIR}/src) 26 | 27 | 28 | set(TEST_DIR ${DUCER_DIR}/test) 29 | 30 | # define the sources of the self test 31 | set(TEST_SOURCES 32 | ${TEST_DIR}/test.cpp 33 | ) 34 | add_executable(ducer_test ${TEST_SOURCES}) 35 | TARGET_LINK_LIBRARIES(ducer_test ${CMAKE_THREAD_LIBS_INIT}) 36 | 37 | add_executable(ducer_gist ${TEST_DIR}/gist.cpp) 38 | TARGET_LINK_LIBRARIES(ducer_gust ${CMAKE_THREAD_LIBS_INIT}) 39 | 40 | # configure unit tests via CTest 41 | enable_testing() 42 | 43 | add_test(NAME RunTests COMMAND ducer_test) 44 | 45 | add_test(NAME ListTests COMMAND ducer_test --list-tests) 46 | set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test case") 47 | 48 | add_test(NAME ListTags COMMAND ducer_test --list-tags) 49 | set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags") 50 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | transducer experiments in c++ 2 | ============================= 3 | 4 | Transducers ignore the represenation of a sequence and instead build algorithm composition around the operations performed on each value. 5 | The core pattern is based on std::accumulate. std::transform, std::copy_if and many others can be implemented in terms of std::accumulate. 6 | ```C++ 7 | template 8 | OutputIterator acc_transform( 9 | InputIterator first1, 10 | InputIterator last1, 11 | OutputIterator result, 12 | UnaryFunction func){ 13 | return std::accumulate(first1, last1, result, [=](auto result, auto v){*result = func(v); return ++result;}); 14 | } 15 | ``` 16 | 17 | References: 18 | - Rich Hickey @ StrangeLoop [YouTube](https://www.youtube.com/watch?v=6mTbuzafcII) 19 | - Rich Hickey [Blog](http://blog.cognitect.com/blog/2014/8/6/transducers-are-coming) 20 | - James Long [Blog](http://jlongster.com/Transducers.js--A-JavaScript-Library-for-Transformation-of-Data) 21 | 22 | Status 23 | ====== 24 | - uses C++14 features. 25 | - implemented transduce, into and compose 26 | - filterer, mapper, summer and println 27 | - the transducers are implementing two arities, next and complete. the arities are implemented with overloading. 28 | 29 | ToDo 30 | ==== 31 | - add early-termination 32 | - show merge, concat and map+merge 33 | 34 | Builds 35 | ====== 36 | 37 | XCode 38 | ----- 39 | ``` 40 | mkdir projects/build 41 | cd projects/build 42 | cmake -G"Xcode" ../CMake -B. 43 | ``` 44 | 45 | OSX 46 | --- 47 | ``` 48 | mkdir projects/build 49 | cd projects/build 50 | cmake -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -B. ../CMake 51 | make 52 | ``` 53 | 54 | Usage examples 55 | ============== 56 | 57 | Functional style. As shown in Rich Hickey's talk. 58 | ```C++ 59 | auto even = [](auto v){return v % 2 == 0;}; 60 | 61 | auto inc = [](auto v){return ++v;}; 62 | 63 | auto source = std::array({1,2,3,4}); 64 | 65 | auto sum = into( 66 | compose( 67 | filterer(even), 68 | mapper(inc), 69 | summer(), 70 | println(std::cout)), 71 | 0L, 72 | source); 73 | ``` 74 | 75 | Object style. Similar to the pattern used by Rx (Reactive Extentions) libraries. 76 | ```C++ 77 | auto even = [](auto v){return v % 2 == 0;}; 78 | 79 | auto inc = [](auto v){return ++v;}; 80 | 81 | auto source = std::array({1,2,3,4}); 82 | 83 | auto sum_of_inc_even = ducer<>(). 84 | filter(even). 85 | map(inc). 86 | sum(); 87 | 88 | auto sum = sum_of_inc_even. 89 | println(std::cout). 90 | into(0L, source); 91 | ``` 92 | 93 | Filterer 94 | ======== 95 | 96 | With C++14 auto lambdas, the filterer can look much like the impl in functional languages. 97 | ```C++ 98 | auto filterer = [](auto pred){ 99 | return [=](auto step) { 100 | return stateless( 101 | [=](auto s, auto v){ 102 | if (pred(v)) { 103 | return step(s, v); 104 | } 105 | return s; 106 | }, 107 | [=](auto s){return step(s);}); 108 | }; 109 | }; 110 | ``` 111 | 112 | Stateless 113 | ========= 114 | 115 | Uses overloading to implement 2 arity step function 116 | ```C++ 117 | template 118 | struct stateless_s 119 | { 120 | Next next; 121 | Complete complete; 122 | 123 | stateless_s(Next next, Complete complete) : next(next), complete(complete) {} 124 | 125 | // complete 126 | template 127 | Acc operator()(Acc acc) const { return complete(acc);} 128 | // next 129 | template 130 | Acc operator()(Acc acc, T v) const { return next(acc, v);} 131 | }; 132 | 133 | auto stateless = [](auto next, auto complete){ 134 | return stateless_s{next, complete}; 135 | }; 136 | ``` 137 | -------------------------------------------------------------------------------- /src/ducer/ducer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "ducer_arities.h" 13 | #include "ducer_filterer.h" 14 | #include "ducer_mapper.h" 15 | #include "ducer_summer.h" 16 | #include "ducer_taker.h" 17 | #include "ducer_println.h" 18 | #include "ducer_pass.h" 19 | #include "ducer_transduce.h" 20 | #include "ducer_into.h" 21 | #include "ducer_compose.h" 22 | 23 | namespace nsducer { 24 | 25 | template 26 | struct ducer_type : public Ducer 27 | { 28 | ducer_type() : Ducer(pass()) {} 29 | ducer_type(Ducer d) : Ducer(d) {} 30 | 31 | template 32 | auto filter(Pred pred) { 33 | auto composed = compose(*this, filterer(pred)); 34 | return ducer_type(composed); 35 | } 36 | 37 | template 38 | auto map(Map map) { 39 | auto composed = compose(*this, mapper(map)); 40 | return ducer_type(composed); 41 | } 42 | 43 | auto sum() { 44 | auto composed = compose(*this, summer()); 45 | return ducer_type(composed); 46 | } 47 | 48 | template 49 | auto println(Stream& stream) { 50 | auto composed = compose(*this, nsducer::println(stream)); 51 | return ducer_type(composed); 52 | } 53 | 54 | template 55 | auto to_vector(Collection collection) { 56 | return into(*this, std::vector(), collection); 57 | } 58 | 59 | template 60 | auto into(To to, Collection collection) { 61 | return nsducer::into(*this, to, collection); 62 | } 63 | }; 64 | 65 | auto ducer = ducer_type<>(); 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/ducer/ducer_arities.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | template 6 | struct stateless_s 7 | { 8 | Next next; 9 | Complete complete; 10 | 11 | stateless_s(Next next, Complete complete) : next(next), complete(complete) {} 12 | 13 | // complete 14 | template 15 | Acc operator()(Acc acc) const { return complete(acc);} 16 | // next 17 | template 18 | Acc operator()(Acc acc, T v) const { return next(acc, v);} 19 | }; 20 | 21 | auto stateless = [](auto next, auto complete){ 22 | return stateless_s{next, complete}; 23 | }; 24 | 25 | template 26 | struct stateful_s 27 | { 28 | std::shared_ptr state; 29 | Next next; 30 | Complete complete; 31 | 32 | stateful_s(State state, Next next, Complete complete) : state(std::make_shared(state)), next(next), complete(complete) {} 33 | 34 | // complete 35 | template 36 | auto operator()(Acc acc) const { return complete(acc, *state);} 37 | // next 38 | template 39 | Acc operator()(Acc acc, T v) const { return next(acc, *state, v);} 40 | }; 41 | 42 | auto stateful = [](auto state, auto next, auto complete){ 43 | return stateful_s{state, next, complete}; 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/ducer/ducer_compose.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | template 6 | auto compose(Ducer ducer) { 7 | return ducer; 8 | } 9 | 10 | template 11 | auto compose(Ducer0 ducer0, DucerN... ducerN) { 12 | return [=](auto step) {return ducer0(compose(ducerN...)(step));}; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/ducer/ducer_filterer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | auto filterer = [](auto pred){ 6 | return [=](auto step) { 7 | return stateless( 8 | [=](auto s, auto v){ 9 | if (pred(v)) { 10 | return step(s, v); 11 | } 12 | return s; 13 | }, 14 | [=](auto s){return step(s);}); 15 | }; 16 | }; 17 | 18 | } -------------------------------------------------------------------------------- /src/ducer/ducer_into.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | template 6 | auto into_for(std::vector acc, V v, int) { 7 | acc.push_back(v); 8 | return acc; 9 | } 10 | 11 | template 12 | auto into_for(Stream acc, V v, int) { 13 | *acc << v << std::endl; 14 | return acc; 15 | } 16 | 17 | template 18 | auto into_for(T acc, V v, ...) { 19 | acc = v; 20 | return acc; 21 | } 22 | 23 | struct into_next 24 | { 25 | template 26 | auto operator()(LHS lhs, RHS rhs) const { 27 | return into_for(lhs, rhs, 0); 28 | } 29 | }; 30 | 31 | auto into = [](auto ducer, auto out, auto in){ 32 | return transduce( 33 | ducer, 34 | stateless( 35 | into_next(), 36 | [](decltype(out) acc){return acc;}), 37 | out, 38 | in); 39 | }; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/ducer/ducer_mapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | auto mapper = [](auto map){ 6 | return [=](auto step) { 7 | return stateless( 8 | [=](auto s, auto v){ 9 | return step(s, map(v)); 10 | }, 11 | [=](auto s){return step(s);}); 12 | }; 13 | }; 14 | 15 | 16 | } -------------------------------------------------------------------------------- /src/ducer/ducer_pass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | auto pass = [](){ 6 | return [=](auto step) { 7 | return step; 8 | }; 9 | }; 10 | 11 | } -------------------------------------------------------------------------------- /src/ducer/ducer_println.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | auto println = [](auto& stream){ 6 | auto pointer = std::addressof(stream); 7 | return [=](auto step) { 8 | typedef decltype(pointer) Pointer; 9 | return stateful( 10 | pointer, 11 | [=](auto s, Pointer stream, auto v){ 12 | *stream << v << std::endl; return step(s, v); 13 | }, 14 | [=](auto s, Pointer){return s;}); 15 | }; 16 | }; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/ducer/ducer_summer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | auto summer = [](){ 6 | return [=](auto step) { 7 | return stateful( 8 | 0L, 9 | [=](auto s, long& sum, auto v){ 10 | sum += v; return s; 11 | }, 12 | [=](auto s, long& sum){return step(s, sum);}); 13 | }; 14 | }; 15 | 16 | } -------------------------------------------------------------------------------- /src/ducer/ducer_taker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | auto taker = [](long count){ 6 | return [=](auto step) { 7 | return stateful( 8 | count, 9 | [=](auto s, long& count, auto v){ 10 | if (--count) {return step(s, v);} return s; 11 | }, 12 | [=](auto s, long& count){return s;}); 13 | }; 14 | }; 15 | 16 | } -------------------------------------------------------------------------------- /src/ducer/ducer_transduce.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nsducer { 4 | 5 | auto transduce = [](auto ducer, auto reduce, auto init, auto collection) { 6 | auto step = ducer(reduce); 7 | for(auto& v : collection) { 8 | init = step(init, v); 9 | } 10 | return step(init); 11 | }; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /test/gist.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | struct stateless_s 12 | { 13 | Next next; 14 | Complete complete; 15 | 16 | stateless_s(Next next, Complete complete) : next(next), complete(complete) {} 17 | 18 | // complete 19 | template 20 | Acc operator()(Acc acc) const { return complete(acc);} 21 | // next 22 | template 23 | Acc operator()(Acc acc, T v) const { return next(acc, v);} 24 | }; 25 | 26 | auto stateless = [](auto next, auto complete){ 27 | return stateless_s{next, complete}; 28 | }; 29 | 30 | 31 | template 32 | struct stateful_s 33 | { 34 | std::shared_ptr state; 35 | Next next; 36 | Complete complete; 37 | 38 | stateful_s(State state, Next next, Complete complete) : state(std::make_shared(state)), next(next), complete(complete) {} 39 | 40 | // complete 41 | template 42 | auto operator()(Acc acc) const { return complete(acc, *state);} 43 | // next 44 | template 45 | Acc operator()(Acc acc, T v) const { return next(acc, *state, v);} 46 | }; 47 | 48 | auto stateful = [](auto state, auto next, auto complete){ 49 | return stateful_s{state, next, complete}; 50 | }; 51 | 52 | 53 | auto filterer = [](auto pred){ 54 | return [=](auto step) { 55 | return stateless( 56 | [=](auto s, auto v){ 57 | if (pred(v)) { 58 | return step(s, v); 59 | } 60 | return s; 61 | }, 62 | [=](auto s){return step(s);}); 63 | }; 64 | }; 65 | 66 | auto mapper = [](auto map){ 67 | return [=](auto step) { 68 | return stateless( 69 | [=](auto s, auto v){ 70 | return step(s, map(v)); 71 | }, 72 | [=](auto s){return step(s);}); 73 | }; 74 | }; 75 | 76 | auto summer = [](){ 77 | return [=](auto step) { 78 | return stateful( 79 | 0L, 80 | [=](auto s, long& sum, auto v){ 81 | sum += v; return s; 82 | }, 83 | [=](auto s, long& sum){return step(s, sum);}); 84 | }; 85 | }; 86 | 87 | auto println = [](auto& stream){ 88 | auto pointer = std::addressof(stream); 89 | return [=](auto step) { 90 | typedef decltype(pointer) Pointer; 91 | return stateful( 92 | pointer, 93 | [=](auto s, Pointer stream, auto v){ 94 | *stream << v << std::endl; return step(s, v); 95 | }, 96 | [=](auto s, Pointer){return s;}); 97 | }; 98 | }; 99 | 100 | auto pass = [](){ 101 | return [=](auto step) { 102 | return step; 103 | }; 104 | }; 105 | 106 | auto transduce = [](auto ducer, auto reduce, auto init, auto collection) { 107 | auto step = ducer(reduce); 108 | for(auto& v : collection) { 109 | init = step(init, v); 110 | } 111 | return step(init); 112 | }; 113 | 114 | template 115 | auto into_for(std::vector acc, V v, int) { 116 | acc.push_back(v); 117 | return acc; 118 | } 119 | 120 | template 121 | auto into_for(Stream acc, V v, int) { 122 | *acc << v << std::endl; 123 | return acc; 124 | } 125 | 126 | template 127 | auto into_for(T acc, V v, ...) { 128 | acc = v; 129 | return acc; 130 | } 131 | 132 | struct into_next 133 | { 134 | template 135 | auto operator()(LHS lhs, RHS rhs) const { 136 | return into_for(lhs, rhs, 0); 137 | } 138 | }; 139 | 140 | auto into = [](auto ducer, auto out, auto in){ 141 | return transduce( 142 | ducer, 143 | stateless( 144 | into_next(), 145 | [](decltype(out) acc){return acc;}), 146 | out, 147 | in); 148 | }; 149 | 150 | template 151 | auto compose(Ducer ducer) { 152 | return ducer; 153 | } 154 | 155 | template 156 | auto compose(Ducer0 ducer0, DucerN... ducerN) { 157 | return [=](auto step) {return ducer0(compose(ducerN...)(step));}; 158 | } 159 | 160 | template 161 | struct ducer : public Ducer 162 | { 163 | ducer() : Ducer(pass()) {} 164 | ducer(Ducer d) : Ducer(d) {} 165 | 166 | template 167 | auto filter(Pred pred) { 168 | auto composed = compose(*this, filterer(pred)); 169 | return ducer(composed); 170 | } 171 | 172 | template 173 | auto map(Map map) { 174 | auto composed = compose(*this, mapper(map)); 175 | return ducer(composed); 176 | } 177 | 178 | auto sum() { 179 | auto composed = compose(*this, summer()); 180 | return ducer(composed); 181 | } 182 | 183 | template 184 | auto println(Stream& stream) { 185 | auto composed = compose(*this, ::println(stream)); 186 | return ducer(composed); 187 | } 188 | 189 | template 190 | auto into(To to, Collection collection) { 191 | return ::into(*this, to, collection); 192 | } 193 | 194 | }; 195 | 196 | auto even = [](auto v){return v % 2 == 0;}; 197 | 198 | auto inc = [](auto v){return ++v;}; 199 | 200 | template 201 | OutputIterator acc_transform( 202 | InputIterator first1, 203 | InputIterator last1, 204 | OutputIterator result, 205 | UnaryFunction func){ 206 | return std::accumulate(first1, last1, result, [=](auto result, auto v){*result = func(v); return ++result;}); 207 | } 208 | 209 | int main() { 210 | 211 | const long required = 8; 212 | 213 | auto source = std::array({1,2,3,4}); 214 | 215 | auto out = std::vector(); 216 | acc_transform(std::begin(source), std::end(source), std::back_inserter(out), [](auto v){return v + 1;}); 217 | for(auto v : out) { 218 | std::cout << v << std::endl; 219 | } 220 | 221 | auto sum_of_inc_even = ducer<>(). 222 | filter(even). 223 | map(inc). 224 | sum(); 225 | 226 | { 227 | auto ss = std::stringstream(); 228 | std::cout << transduce(sum_of_inc_even, 229 | stateless( 230 | [](auto acc, auto v){*acc << v << std::endl; return acc;}, 231 | [](auto acc){return acc;}), 232 | std::addressof(ss), source)->str(); 233 | } 234 | 235 | { 236 | auto actual = into( 237 | compose( 238 | filterer(even), 239 | mapper(inc), 240 | summer(), 241 | println(std::cout)), 242 | 0L, 243 | source); 244 | assert(actual == required); 245 | } 246 | 247 | { 248 | auto actual = sum_of_inc_even. 249 | println(std::cout). 250 | into(0L, source); 251 | assert(actual == required); 252 | } 253 | 254 | { 255 | auto actual = into( 256 | compose( 257 | sum_of_inc_even, 258 | println(std::cout)), 259 | 0L, 260 | source); 261 | assert(actual == required); 262 | } 263 | 264 | { 265 | auto ss = std::stringstream(); 266 | std::cout << sum_of_inc_even. 267 | into(std::addressof(ss), source)->str(); 268 | auto sactual = ss.str(); 269 | auto srequired = std::string("8\n"); 270 | assert(sactual == srequired); 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include "ducer/ducer.h" 8 | namespace d = nsducer; 9 | 10 | auto even = [](auto v){return v % 2 == 0;}; 11 | auto odd = [](auto v){return v % 2 != 0;}; 12 | auto inc = [](auto v){return ++v;}; 13 | 14 | SCENARIO("ducer", "[ducer][operators]"){ 15 | GIVEN("a test"){ 16 | 17 | WHEN("sum of even number in 1-4 each incremented by 1"){ 18 | 19 | long actual; 20 | const long required = 8; 21 | 22 | auto source = std::array({1,2,3,4}); 23 | 24 | auto sum_of_inc_even = d::ducer. 25 | filter(even). 26 | map(inc). 27 | sum(); 28 | 29 | THEN("the output must be 8"){ 30 | auto ss = std::stringstream(); 31 | std::cout << sum_of_inc_even. 32 | into(std::addressof(ss), source)->str(); 33 | auto actual = ss.str(); 34 | auto required = std::string("8\n"); 35 | REQUIRE(actual == required); 36 | } 37 | 38 | THEN("the output must be 8"){ 39 | actual = d::into( 40 | d::compose( 41 | d::filterer(even), 42 | d::mapper(inc), 43 | d::summer(), 44 | d::println(std::cout)), 45 | 0L, 46 | source); 47 | REQUIRE(actual == required); 48 | } 49 | 50 | THEN("the output must be 8"){ 51 | actual = sum_of_inc_even. 52 | println(std::cout). 53 | into(0L, source); 54 | REQUIRE(actual == required); 55 | } 56 | 57 | THEN("the output must be 8"){ 58 | actual = d::into( 59 | d::compose( 60 | sum_of_inc_even, 61 | d::println(std::cout)), 62 | 0L, 63 | source); 64 | REQUIRE(actual == required); 65 | } 66 | } 67 | } 68 | } 69 | --------------------------------------------------------------------------------