From 1979161431de0627b0d4f6f2c12478a25ec9c227 Mon Sep 17 00:00:00 2001 From: aaron-jack-manning Date: Fri, 17 Jun 2022 22:01:01 +1000 Subject: [PATCH] first commit --- .gitignore | 1 + Cargo.lock | 7 ++ Cargo.toml | 8 ++ README.md | 3 + src/lib.rs | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 264 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..935be5a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "lazy-seq" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e24d621 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "lazy-seq" +version = "0.1.0" +edition = "2021" +description = "A crate for constructing lazily evaluated sequences." +license = "MIT OR Apache-2.0" + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd61d9c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Lazy Seq + +This crate contains Seq which is a lazily evaluated sequence of functions. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4de9f85 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,245 @@ +//! # Lazy Seq +//! +//! `lazy_seq` is a crate containing tools for lazily evaluated +//! collections, specifically Seq which is a collection of lazily +//! evaluated functions, and Lazy, which is the primitive used for lazy +//! evaluation. + +/// Stores an operation which can be evaluated when required. +pub struct Lazy { + f : Option T>>, + val : Option, +} + +impl Lazy { + /// Constructs a new `Lazy` from a closure implementing `FnOnce() -> T`. + pub fn new T + 'static>(f : F) -> Lazy { + Lazy { + f : Some(Box::new(f)), + val : None, + } + } + + /// Forces evaluation of the underlying function. + pub fn force(&mut self) { + if self.f.is_some() { + let mut f = None; + std::mem::swap(&mut f, &mut self.f); + + let f = f.unwrap(); + let result = (f)(); + self.val = Some(result); + } + } + + /// Forces evaluation of the underlying function and returns the inside value, consuming + /// the `Lazy`. + pub fn into_inner(mut self) -> T { + self.force(); + let Lazy { f : _ , val } = self; + val.unwrap() + } + + /// Returns an immutable reference to the result of the function. + pub fn as_ref(&mut self) -> &T { + self.force(); + self.val.as_ref().unwrap() + } +} + +impl AsMut for Lazy { + /// Returns a mutable reference to the result of the function. + fn as_mut(&mut self) -> &mut T { + self.force(); + self.val.as_mut().unwrap() + } +} + +impl Default for Lazy + where T : Default { + fn default() -> Self { + Lazy { + f : None, + val : Some(T::default()) + } + } +} + +/// A lazily evaluated sequence of functions. +pub struct Seq(Vec>); + +impl Seq { + /// Constructs a new, empty `Seq`. + pub fn new() -> Seq { + Seq(Vec::new()) + } + + /// Returns `true` if the sequence contains no elements. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + // Returns the number of elements the sequence can hold without reallocating. + pub fn capacity(&self) -> usize { + self.0.capacity() + } + + /// Returns the number of elements in the sequence, also referred to as its ‘length’. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Constructs a new, empty `Seq` with the specified capacity. + pub fn with_capacity(capacity : usize) -> Seq { + Seq(Vec::with_capacity(capacity)) + } + + /// Appends the provided function to the back of the collection. + pub fn push T + 'static>(&mut self, value : F) { + self.0.push(Lazy::new(value)) + } + + /// Forces evaluation of the last function in the sequence, returning the result. + pub fn pop(&mut self) -> Option { + let lazy = self.0.pop(); + lazy.map(|l| l.into_inner()) + } + + /// Evaluates all unevaluated functions within the Seq. + pub fn force_all(&mut self) { + for i in 0..self.0.len() { + self.0[i].force() + } + } + + /// Converts a `Seq` to a `Vec` by forcing evaluation of all functions. + pub fn to_vec(self) -> Vec { + let Seq(vec) = self; + + vec + .into_iter() + .map(|x| x.into_inner()) + .collect() + } + + /// Converts a `Seq` to a `Vec>` without evaluating any additional + /// functions. + pub fn to_lazy_vec(self) -> Vec> { + let Seq(vec) = self; + vec + } + + /// Gets an immutable reference to the value at the specified index. + pub fn get(&mut self, index : usize) -> Option<&T> { + self.0.get_mut(index).map(|l| { l.force(); l.as_ref() }) + } + + /// Gets a mutable reference to the value at the specified index. + pub fn get_mut(&mut self, index : usize) -> Option<&mut T> { + self.0.get_mut(index).map(|l| { l.force(); l.as_mut() }) + } +} + +impl From T>>> for Seq { + fn from(vec : Vec T>>) -> Self { + Seq( + vec + .into_iter() + .map(|b| Lazy { f : Some(b), val : None }) + .collect() + ) + } +} + +impl From> for Seq { + fn from(vec : Vec) -> Self { + Seq( + vec + .into_iter() + .map(|v| Lazy { f : None, val : Some(v) }) + .collect() + ) + } +} + +impl Default for Seq { + fn default() -> Self { + Self::new() + } +} + +/// Constructs a Seq from a literal in a similar way that vec! does for Vec. +#[macro_export] +macro_rules! seq { + () => ( + lazy_seq::Seq::new() + ); + ($elem:expr; $n:expr) => ( + { + let mut seq = Seq::with_capacity($n); + for _ in 0..$n { + seq.push($elem); + } + seq + } + ); + ($($x:expr),+ $(,)?) => ( + { + let mut seq = Seq::new(); + $(seq.push($x);)* + seq + } + ); +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + use std::cell::RefCell; + use crate::{Lazy, Seq, seq}; + #[test] + pub fn lazy_correct() { + let counter = Rc::new(RefCell::new(0)); + let counter_new = counter.clone(); + let mut lazy = Lazy::new( + move || { + *counter_new.borrow_mut() += 1; + 5 + 5 + }); + let result = lazy.as_ref(); + assert_eq!(10, *result); + } + + #[test] + pub fn seq_correct() { + + let counter = Rc::new(RefCell::new(0)); + let counter_new = counter.clone(); + + let mut seq = seq![ + || { 0 }; 9 + ]; + + assert_eq!(9, seq.len()); + + seq.push(move || { + *counter_new.borrow_mut() += 1; + println!("{:?}", counter_new); + 5 + 5 + }); + + assert_eq!(10, seq.len()); + + assert_eq!(0, *counter.borrow()); + + let _ = seq.get(9).unwrap(); + + assert_eq!(1, *counter.borrow()); + + seq.force_all(); + let result = seq.pop(); + assert_eq!(9, seq.len()); + assert_eq!(1, *counter.borrow()); + assert_eq!(10, result.unwrap()); + } +}