Add exact argument to array.zip (#4030)

This commit is contained in:
T0mstone 2024-05-06 16:02:27 +02:00 committed by GitHub
parent 329b0f9b8d
commit c4c53ab52e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 52 additions and 10 deletions

View File

@ -8,14 +8,14 @@ use ecow::{eco_format, EcoString, EcoVec};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::diag::{bail, At, SourceDiagnostic, SourceResult, StrResult};
use crate::engine::Engine;
use crate::eval::ops;
use crate::foundations::{
cast, func, repr, scope, ty, Args, Bytes, CastInfo, Context, Dict, FromValue, Func,
IntoValue, Reflect, Repr, Str, Value, Version,
};
use crate::syntax::Span;
use crate::syntax::{Span, Spanned};
/// Create a new [`Array`] from values.
#[macro_export]
@ -482,9 +482,14 @@ impl Array {
#[func]
pub fn zip(
self,
/// The real arguments (the other arguments are just for the docs, this
/// function is a bit involved, so we parse the arguments manually).
/// The real arguments (the `others` arguments are just for the docs, this
/// function is a bit involved, so we parse the positional arguments manually).
args: &mut Args,
/// Whether all arrays have to have the same length.
/// For example, `(1, 2).zip((1, 2, 3), exact: true)` produces an error.
#[named]
#[default(false)]
exact: bool,
/// The arrays to zip with.
#[external]
#[variadic]
@ -499,7 +504,16 @@ impl Array {
// Fast path for just two arrays.
if remaining == 1 {
let other = args.expect::<Array>("others")?;
let Spanned { v: other, span: other_span } =
args.expect::<Spanned<Array>>("others")?;
if exact && self.len() != other.len() {
bail!(
other_span,
"second array has different length ({}) from first array ({})",
other.len(),
self.len()
);
}
return Ok(self
.into_iter()
.zip(other)
@ -509,11 +523,29 @@ impl Array {
// If there is more than one array, we use the manual method.
let mut out = Self::with_capacity(self.len());
let mut iterators = args
.all::<Array>()?
.into_iter()
.map(|i| i.into_iter())
.collect::<Vec<_>>();
let arrays = args.all::<Spanned<Array>>()?;
if exact {
let errs = arrays
.iter()
.filter(|sp| sp.v.len() != self.len())
.map(|Spanned { v, span }| {
SourceDiagnostic::error(
*span,
eco_format!(
"array has different length ({}) from first array ({})",
v.len(),
self.len()
),
)
})
.collect::<EcoVec<_>>();
if !errs.is_empty() {
return Err(errs);
}
}
let mut iterators =
arrays.into_iter().map(|i| i.v.into_iter()).collect::<Vec<_>>();
for this in self {
let mut row = Self::with_capacity(1 + iterators.len());

View File

@ -350,6 +350,7 @@
#test((1,).zip(()), ())
#test((1,).zip((2,)), ((1, 2),))
#test((1, 2).zip((3, 4)), ((1, 3), (2, 4)))
#test((1, 2).zip((3, 4), exact: true), ((1, 3), (2, 4)))
#test((1, 2, 3, 4).zip((5, 6)), ((1, 5), (2, 6)))
#test(((1, 2), 3).zip((4, 5)), (((1, 2), 4), (3, 5)))
#test((1, "hi").zip((true, false)), ((1, true), ("hi", false)))
@ -359,6 +360,15 @@
#test((1, 2, 3).zip(), ((1,), (2,), (3,)))
#test(array.zip(()), ())
--- array-zip-exact-error ---
// Error: 13-22 second array has different length (3) from first array (2)
#(1, 2).zip((1, 2, 3), exact: true)
--- array-zip-exact-multi-error ---
// Error: 13-22 array has different length (3) from first array (2)
// Error: 24-36 array has different length (4) from first array (2)
#(1, 2).zip((1, 2, 3), (1, 2, 3, 4), exact: true)
--- array-enumerate ---
// Test the `enumerate` method.
#test(().enumerate(), ())