From 3bca25e8dfa61d4bde39f08ff9bd7202782cbb10 Mon Sep 17 00:00:00 2001 From: Philipp Rehner <69816385+prehner@users.noreply.github.com> Date: Wed, 8 Jun 2022 09:39:42 +0200 Subject: [PATCH] Add iterator functionalities to SIArray1 (#48) * Add iterator functionalities to SIArray1 * make implementation more generic * fix anomalous capitalization * Update files for release --- CHANGELOG.md | 5 ++ Cargo.toml | 2 +- si-units/Cargo.toml | 2 +- src/lib.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++ src/si_fmt.rs | 4 +- 5 files changed, 127 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e9d69..fd73eb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.5.1] - 2022-06-08 +### Added +- Added implementations of `IntoIterator` for every `Quantity` with iteratable inner types. [#48](https://github.com/itt-ustutt/quantity/pull/48) +- Implemented `FromIterator` for `QuantityArray1`, enabling the use of `collect()` for quantity arrays. [#48](https://github.com/itt-ustutt/quantity/pull/48) + ## [0.5.0] - 2022-03-09 ### Packaging - Updated `pyo3` and `numpy` dependencies to 0.16. diff --git a/Cargo.toml b/Cargo.toml index 8a233fc..abf178c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "quantity" -version = "0.5.0" +version = "0.5.1" authors = ["Philipp Rehner ", "Gernot Bauer "] edition = "2018" diff --git a/si-units/Cargo.toml b/si-units/Cargo.toml index a3e8595..bf94514 100644 --- a/si-units/Cargo.toml +++ b/si-units/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "si-units" -version = "0.5.0" +version = "0.5.1" authors = ["Philipp Rehner ", "Gernot Bauer "] edition = "2018" diff --git a/src/lib.rs b/src/lib.rs index 35a9f0c..add7ca7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ use ndarray::*; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt; +use std::iter::FromIterator; use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; @@ -1089,3 +1090,120 @@ impl QuantityArray1 { Self::from_shape_fn(vec.len(), |i| vec[i]) } } + +pub struct QuantityIter { + inner: I, + unit: U, +} + +impl<'a, I: Iterator, U: Copy> Iterator for QuantityIter { + type Item = QuantityScalar; + + fn next(&mut self) -> Option { + self.inner.next().map(|value| QuantityScalar { + value: *value, + unit: self.unit, + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a, I: Iterator + ExactSizeIterator, U: Copy> ExactSizeIterator + for QuantityIter +{ + fn len(&self) -> usize { + self.inner.len() + } +} + +impl<'a, I: Iterator + DoubleEndedIterator, U: Copy> DoubleEndedIterator + for QuantityIter +{ + fn next_back(&mut self) -> Option { + self.inner.next_back().map(|value| QuantityScalar { + value: *value, + unit: self.unit, + }) + } +} + +impl<'a, F, U: Copy> IntoIterator for &'a Quantity +where + &'a F: IntoIterator, +{ + type Item = QuantityScalar; + type IntoIter = QuantityIter<<&'a F as IntoIterator>::IntoIter, U>; + + fn into_iter(self) -> Self::IntoIter { + QuantityIter { + inner: (&self.value).into_iter(), + unit: self.unit, + } + } +} + +impl FromIterator> for QuantityArray1 { + fn from_iter(iter: I) -> Self + where + I: IntoIterator>, + { + let (value, unit) = + iter.into_iter() + .fold((Vec::new(), U::DIMENSIONLESS), |(mut value, unit), q| { + value.push(q.value); + if unit != q.unit && unit != U::DIMENSIONLESS { + panic!("Inconsistent units {unit} and {}", q.unit); + } + (value, q.unit) + }); + let value = Array1::from_vec(value); + Self { value, unit } + } +} + +#[cfg(test)] +mod test { + use crate::si::*; + use std::iter::once; + + #[test] + fn test_into_iter() { + let x = SIArray1::linspace(1.0 * METER, 3.0 * METER, 6).unwrap(); + for q in &x { + println!("{q}"); + } + let mut x_iter = x.into_iter(); + assert_eq!(x_iter.next(), Some(1.0 * METER)); + assert_eq!(x_iter.next(), Some(1.4 * METER)); + assert_eq!(x_iter.next(), Some(1.8 * METER)); + assert_eq!(x_iter.next(), Some(2.2 * METER)); + assert_eq!(x_iter.next(), Some(2.6 * METER)); + assert_eq!(x_iter.next(), Some(3.0 * METER)); + assert_eq!(x_iter.next(), None); + } + + #[test] + fn test_collect_vec() { + let vec: Vec<_> = once(KELVIN).chain(once(METER)).collect(); + assert_eq!(vec[0], KELVIN); + assert_eq!(vec[1], METER); + } + + #[test] + #[should_panic(expected = "Inconsistent units K and m")] + fn test_collect_array_wrong() { + let arr: SIArray1 = once(KELVIN).chain(once(METER)).collect(); + println!("{arr}"); + } + + #[test] + fn test_collect_array_correct() { + let arr: SIArray1 = once(KELVIN).chain(once(25.0 * CELSIUS)).collect(); + println!("{arr}"); + assert_eq!(arr.get(0), KELVIN); + assert_eq!(arr.get(1), 25.0 * CELSIUS); + } +} diff --git a/src/si_fmt.rs b/src/si_fmt.rs index 4f7792f..8a49e74 100644 --- a/src/si_fmt.rs +++ b/src/si_fmt.rs @@ -165,7 +165,7 @@ fn unit_to_latex(symbols: &[&str], exponents: &[i8], prefix: Option<&str>) -> St let num_st = unit_to_latex_product(num); let den_st = unit_to_latex_product(den); match (num_st, den_st) { - (None, None) => format!(""), + (None, None) => String::new(), (Some(num), None) => format!("\\mathrm{{{}}}", num), (None, Some(den)) => format!("\\mathrm{{\\frac{{1}}{{{}}}}}", den), (Some(num), Some(den)) => format!("\\mathrm{{\\frac{{{}}}{{{}}}}}", num, den), @@ -282,7 +282,7 @@ fn insert_derived_unit(map: &mut HashMap, s: &'static str) let unit = unit.unwrap(); map.insert( unit.unit, - (unit, s.replace("*", ""), has_prefix, symbols, exponents), + (unit, s.replace('*', ""), has_prefix, symbols, exponents), ); }