fp_rust/
fp.rs

1/*!
2In this module there're implementations & tests
3of general functional programming features.
4*/
5
6/**
7Pipe functions.
8
9*NOTE*: Credit https://stackoverflow.com/questions/45786955/how-to-compose-functions-in-rust
10*/
11#[macro_export]
12macro_rules! pipe {
13    ( $last:expr ) => { $last };
14    ( $head:expr, $($tail:expr), +) => {
15        compose_two($head, pipe!($($tail),+))
16    };
17}
18
19/**
20Compose functions.
21
22*NOTE*: Credit https://stackoverflow.com/questions/45786955/how-to-compose-functions-in-rust
23*/
24#[macro_export]
25macro_rules! compose {
26    ( $last:expr ) => { $last };
27    ( $head:expr, $($tail:expr), +) => {
28        compose_two(compose!($($tail),+), $head)
29    };
30}
31
32/**
33`Spread` the variadic arguments and call the given funciton.
34*/
35#[macro_export]
36macro_rules! spread_and_call {
37    ($func:expr, $($x:expr), *) => {
38        $func($($x), *);
39    };
40}
41
42/**
43`Partial` application macro with variadic arguments for a pre-defined function,
44and currying the lastest one argument by returning closure.
45*/
46#[macro_export]
47macro_rules! partial_left_last_one {
48    ($func:expr, $($x:expr), *) => {
49        |v| spread_and_call!($func, $($x), *, v)
50    };
51}
52
53/**
54`Map` macro for `Vec<T>`, in currying ways by `partial_left_last_one!`().
55*/
56#[macro_export]
57macro_rules! map {
58    ($func:expr) => {
59        partial_left_last_one!(map, $func)
60    };
61}
62
63/**
64`Filter` macro for `Vec<T>`, in currying ways by `partial_left_last_one!`().
65*/
66#[macro_export]
67macro_rules! filter {
68    ($func:expr) => {
69        partial_left_last_one!(filter, $func)
70    };
71}
72
73/**
74`Reduce` macro for `Vec<T>`, in currying ways by `partial_left_last_one!`().
75*/
76#[macro_export]
77macro_rules! reduce {
78    ($func:expr) => {
79        partial_left_last_one!(reduce, $func)
80    };
81}
82
83/**
84`Foldl` macro for `Vec<T>`, in currying ways by `partial_left_last_one!`().
85*/
86#[macro_export]
87macro_rules! foldl {
88    ($func:expr, $second:expr) => {
89        partial_left_last_one!(foldl, $func, $second)
90    };
91}
92
93/**
94`Foldr` macro for `Vec<T>`, in currying ways by `partial_left_last_one!`().
95*/
96#[macro_export]
97macro_rules! foldr {
98    ($func:expr, $second:expr) => {
99        partial_left_last_one!(foldr, $func, $second)
100    };
101}
102
103/**
104`Reverse` macro for `Vec<T>`, in currying ways.
105*/
106#[macro_export]
107macro_rules! reverse {
108    () => {
109        |v| reverse(v)
110    };
111}
112
113/**
114`Contains` macro for `Vec<T>`, in currying ways by `partial_left_last_one!`().
115*/
116#[macro_export]
117macro_rules! contains {
118    ($x:expr) => {
119        partial_left_last_one!(contains, $x)
120    };
121}
122
123/**
124Compose two functions into one.
125Return `f(g(x))`
126
127# Arguments
128
129* `f` - The given `FnOnce`.
130* `g` - The given `FnOnce`.
131
132*NOTE*: Credit https://stackoverflow.com/questions/45786955/how-to-compose-functions-in-rust
133*/
134#[inline]
135pub fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl FnOnce(A) -> C
136where
137    F: FnOnce(A) -> B,
138    G: FnOnce(B) -> C,
139{
140    move |x| g(f(x))
141}
142
143#[inline]
144pub fn map<T, B>(f: impl FnMut(T) -> B, v: Vec<T>) -> Vec<B> {
145    v.into_iter().map(f).collect::<Vec<B>>()
146}
147
148#[inline]
149pub fn filter<'r, T: 'r>(f: impl FnMut(&T) -> bool, v: Vec<T>) -> Vec<T> {
150    v.into_iter().filter(f).into_iter().collect::<Vec<T>>()
151}
152
153#[inline]
154pub fn foldl<T, B>(f: impl FnMut(B, T) -> B, initial: B, v: Vec<T>) -> B {
155    v.into_iter().fold(initial, f)
156}
157
158#[inline]
159pub fn foldr<T, B>(f: impl FnMut(B, T) -> B, initial: B, v: Vec<T>) -> B {
160    v.into_iter().rev().fold(initial, f)
161}
162
163#[inline]
164pub fn reverse<T>(v: Vec<T>) -> Vec<T> {
165    v.into_iter().rev().collect::<Vec<T>>()
166}
167
168#[inline]
169pub fn contains<T: PartialEq>(x: &T, v: Vec<T>) -> bool {
170    v.contains(x)
171}
172
173/**
174Implementations of `ECMASript`-like `reduce`()
175
176# Arguments
177
178* `T` - The generic type of data.
179
180*NOTE*: Credit https://github.com/dtolnay/reduce
181*/
182pub trait Reduce<T> {
183    fn reduce<F>(self, f: F) -> Option<T>
184    where
185        Self: Sized,
186        F: FnMut(T, T) -> T;
187}
188
189impl<T, I> Reduce<T> for I
190where
191    I: Iterator<Item = T>,
192{
193    #[inline]
194    fn reduce<F>(mut self, f: F) -> Option<T>
195    where
196        Self: Sized,
197        F: FnMut(T, T) -> T,
198    {
199        self.next().map(|first| self.fold(first, f))
200    }
201}
202
203#[inline]
204pub fn reduce<'r, T: 'r>(f: impl FnMut(T, T) -> T, v: Vec<T>) -> Option<T> {
205    Reduce::reduce(v.into_iter(), f)
206}
207
208#[test]
209fn test_compose() {
210    let add = |x| x + 2;
211    let multiply = |x| x * 3;
212    let divide = |x| x / 2;
213
214    let result = (compose!(add, multiply, divide))(10);
215    assert_eq!(17, result);
216    println!("Composed FnOnce Result is {}", result);
217
218    let result = (pipe!(add, multiply, divide))(10);
219    assert_eq!(18, result);
220    println!("Piped FnOnce Result is {}", result);
221}
222
223#[test]
224fn test_map_reduce_filter() {
225    let result =
226        (compose!(reduce!(|a, b| a * b), filter!(|x| *x < 6), map!(|x| x * 2)))(vec![1, 2, 3, 4]);
227    assert_eq!(Some(8), result);
228    println!("test_map_reduce_filter Result is {:?}", result);
229}
230
231#[test]
232fn test_foldl_foldr() {
233    // foldl!(f, initial)
234    let result = (compose!(
235        foldl!(
236            |a, b| {
237                if a < 4 {
238                    return a + b;
239                }
240                return a;
241            },
242            0
243        ),
244        filter!(|x| *x < 6),
245        map!(|x| x * 2)
246    ))(vec![1, 2, 3, 4]);
247    assert_eq!(6, result);
248    println!("foldl Result is {:?}", result);
249
250    // foldr!(f, initial)
251    let result = (compose!(
252        foldr!(
253            |a, b| {
254                if a < 4 {
255                    return a + b;
256                }
257                return a;
258            },
259            0
260        ),
261        filter!(|x| *x < 6),
262        map!(|x| x * 2)
263    ))(vec![1, 2, 3, 4]);
264    assert_eq!(4, result);
265    println!("foldr Result is {:?}", result);
266}
267
268#[test]
269fn test_contains() {
270    assert_eq!(true, contains!(&4)(vec![1, 2, 3, 4]));
271}
272
273#[test]
274fn test_reverse() {
275    assert_eq!(vec![4, 3, 2, 1], reverse!()(vec![1, 2, 3, 4]));
276}