Skip to main content

fp_collections/
list.rs

1#![macro_use]
2use std::cmp::Ordering;
3use std::slice::Iter;
4
5#[derive(PartialEq, Eq, Clone, Debug)]
6pub enum List<T> {
7    Cons(T, Box<List<T>>),
8    Nil,
9}
10
11pub use List::{Cons, Nil};
12
13#[macro_export]
14macro_rules! ls[
15    []                       => (List::Nil);
16    [$x:expr]                => (List::Cons($x, Box::new(List::Nil)));
17    [$x:expr, $($xs:expr),+] => (List::Cons($x, Box::new(ls![$($xs),+])));
18];
19
20impl<T: Clone> From<Iter<'_, T>> for List<T> {
21    fn from(iter: Iter<'_, T>) -> Self {
22        let mut list = ls![];
23        for x in iter {
24            list = list.append(x.clone());
25        }
26
27        list
28    }
29}
30
31impl<T: Clone> From<&[T]> for List<T> {
32    fn from(array: &[T]) -> Self {
33        List::from(array.iter()).map(|x| x)
34    }
35}
36
37impl<T: Eq> List<T> {
38    pub fn new<A>() -> Self {
39        Nil
40    }
41
42    pub fn is_empty(&self) -> bool {
43        self.eq(&Nil)
44    }
45    pub fn null(&self) -> bool {
46        self.is_empty()
47    }
48
49    pub fn len(&self) -> u32 {
50        fn aux<T>(xs: &List<T>, size: u32) -> u32 {
51            match *xs {
52                Nil => size,
53                Cons(_, ref tail) => aux(tail, size + 1),
54            }
55        };
56
57        aux(self, 0)
58    }
59}
60
61impl<T: Clone> List<T> {
62    pub fn prepend(self, x: T) -> Self {
63        Cons(x, Box::new(self))
64    }
65
66    pub fn append(self, item: T) -> Self {
67        match self {
68            Nil => ls![item],
69            Cons(head, tail) => tail.append(item).prepend(head),
70        }
71    }
72
73    pub fn concat(self, other: Self) -> Self {
74        match self {
75            Nil => other,
76            Cons(head, tail) => tail.concat(other).prepend(head),
77        }
78    }
79
80    pub fn get(&self, index: u32) -> Option<&T> {
81        match self {
82            Nil => None,
83            Cons(head, _) if index == 0 => Some(head),
84            Cons(_, ref tail) => tail.get(index - 1),
85        }
86    }
87
88    pub fn head(&self) -> Option<&T> {
89        self.get(0)
90    }
91
92    pub fn tail(&self) -> Self {
93        match self {
94            Nil => (*self).clone(),
95            Cons(_, ref tail) => unsafe {
96                let t = (*tail).clone();
97                (*Box::into_raw(t)).clone()
98            },
99        }
100    }
101
102    pub fn foldl<R>(self, func: impl Fn(R, T) -> R + Copy, init: R) -> R {
103        match self {
104            Nil => init,
105            Cons(x, tail) => tail.foldl(func, func(init, x)),
106        }
107    }
108
109    pub fn foldr<R>(self, func: impl Fn(T, R) -> R + Copy, init: R) -> R {
110        match self {
111            Nil => init,
112            Cons(x, tail) => func(x, tail.foldr(func, init)),
113        }
114    }
115
116    pub fn reverse(self) -> List<T> {
117        fn aux<T>(xs: List<T>, ys: List<T>) -> List<T> {
118            match ys {
119                Nil => xs,
120                Cons(y, tail) => aux(Cons(y, Box::new(xs)), *tail),
121            }
122        };
123        aux(Nil, self)
124    }
125
126    pub fn map<R>(self, func: impl Fn(T) -> R + Copy) -> List<R> {
127        match self {
128            Nil => Nil,
129            Cons(x, tail) => Cons(func(x), Box::new(tail.map(func))),
130        }
131    }
132
133    pub fn filter(self, func: impl Fn(&T) -> bool + Copy) -> Self {
134        match self {
135            Nil => self,
136            Cons(x, tail) => {
137                if func(&x) {
138                    Cons(x, Box::new(tail.filter(func)))
139                } else {
140                    tail.filter(func)
141                }
142            }
143        }
144    }
145
146    fn chop(self, ign: u32) -> Self {
147        if ign == 0 {
148            self
149        } else {
150            match self {
151                Cons(_, tx) => tx.chop(ign - 1),
152                _ => panic!("TODO: meaningful error message"),
153            }
154        }
155    }
156
157    pub fn qsort_by(self, cmpfn: impl Fn(&T, &T) -> Ordering + Copy) -> Self {
158        match self {
159            Nil => self,
160            Cons(head, tail) => {
161                let smaller = tail
162                    .clone()
163                    .filter(|x| cmpfn(x, &head) == Ordering::Less)
164                    .qsort_by(cmpfn);
165                let bigger = tail
166                    .filter(|x| cmpfn(x, &head) != Ordering::Less)
167                    .qsort_by(cmpfn);
168
169                smaller.append(head).concat(bigger)
170            }
171        }
172    }
173}
174
175impl<T> List<T>
176where
177    T: Copy,
178    T: Ord,
179{
180    pub fn qsort(self) -> Self {
181        self.qsort_by(|x, y| x.cmp(y))
182    }
183
184    pub fn sort(&self) -> Self {
185        self.sort_by(|x, y| x.cmp(y))
186    }
187
188    pub fn sort_by(&self, cmpfn: impl Fn(&T, &T) -> Ordering + Copy) -> Self {
189        let ln = self.len();
190        if ln <= 1 {
191            return self.clone();
192        }
193        let l1 = self.clone().chop(ln / 2);
194        let l2 = self.clone().reverse().chop(ln - (ln / 2));
195        l1.sort_by(cmpfn).merge(cmpfn, &l2.sort_by(cmpfn))
196    }
197
198    pub fn partition(self, p: fn(T) -> bool) -> (Self, Self) {
199        fn aux<T: Clone + Copy + Eq + Ord>(
200            yes: List<T>,
201            no: List<T>,
202            p: fn(T) -> bool,
203            xs: List<T>,
204        ) -> (List<T>, List<T>) {
205            match xs {
206                Nil => (yes, no),
207                Cons(x, tx) => {
208                    if p(x) {
209                        // By doing aux(Cons(x, Box::new(yes)), no, p, *tx) [efficent]
210                        // We get the yes list in the reverse order, but if
211                        // we want of preserve order we need to append [not efficient]
212                        aux(yes.append(x), no, p, *tx)
213                    } else {
214                        // aux(yes, Cons(x, Box::new(no)), p, *tx)
215                        aux(yes, no.append(x), p, *tx)
216                    }
217                }
218            }
219        };
220        aux(Nil, Nil, p, self)
221    }
222
223    pub fn merge(&self, cmpfn: impl Fn(&T, &T) -> Ordering + Copy, ys: &List<T>) -> Self {
224        match (self, ys) {
225            (Nil, l) => l.clone(),
226            (l, Nil) => l.clone(),
227            (Cons(x, tx), Cons(y, ty)) => {
228                let cmp = cmpfn(x, y);
229                if cmp == Ordering::Equal || cmp == Ordering::Less {
230                    Cons(*x, Box::new(ys.merge(cmpfn, &**tx)))
231                } else {
232                    Cons(*y, Box::new(self.merge(cmpfn, &**ty)))
233                }
234            }
235        }
236    }
237}