mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 19:37:45 +02:00
Added math product
support (#2249)
* added math product: working initial impl * resolving merge conflicts * impl std::ops::Mul for Filesize * rebased with main; refactored product implementation * fixed error msg, added docs for math product
This commit is contained in:
@ -419,6 +419,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(MathStddev),
|
||||
whole_stream_command(MathSummation),
|
||||
whole_stream_command(MathVariance),
|
||||
whole_stream_command(MathProduct),
|
||||
// File format output
|
||||
whole_stream_command(To),
|
||||
whole_stream_command(ToCSV),
|
||||
|
@ -199,8 +199,8 @@ pub(crate) use last::Last;
|
||||
pub(crate) use lines::Lines;
|
||||
pub(crate) use ls::Ls;
|
||||
pub(crate) use math::{
|
||||
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathStddev,
|
||||
MathSummation, MathVariance,
|
||||
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathProduct,
|
||||
MathStddev, MathSummation, MathVariance,
|
||||
};
|
||||
pub(crate) use merge::Merge;
|
||||
pub(crate) use mkdir::Mkdir;
|
||||
|
@ -5,6 +5,7 @@ pub mod max;
|
||||
pub mod median;
|
||||
pub mod min;
|
||||
pub mod mode;
|
||||
pub mod product;
|
||||
pub mod stddev;
|
||||
pub mod sum;
|
||||
pub mod variance;
|
||||
@ -19,6 +20,7 @@ pub use max::SubCommand as MathMaximum;
|
||||
pub use median::SubCommand as MathMedian;
|
||||
pub use min::SubCommand as MathMinimum;
|
||||
pub use mode::SubCommand as MathMode;
|
||||
pub use product::SubCommand as MathProduct;
|
||||
pub use stddev::SubCommand as MathStddev;
|
||||
pub use sum::SubCommand as MathSummation;
|
||||
pub use variance::SubCommand as MathVariance;
|
||||
|
126
crates/nu-cli/src/commands/math/product.rs
Normal file
126
crates/nu-cli/src/commands/math/product.rs
Normal file
@ -0,0 +1,126 @@
|
||||
use crate::commands::math::reducers::{reducer_for, Reduce};
|
||||
use crate::commands::math::utils::run_with_function;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::{convert_number_to_u64, Number},
|
||||
Primitive, Signature, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math product"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("math product")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Finds the product of a list of numbers or tables"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
run_with_function(
|
||||
RunnableContext {
|
||||
input: args.input,
|
||||
registry: registry.clone(),
|
||||
shell_manager: args.shell_manager,
|
||||
host: args.host,
|
||||
ctrl_c: args.ctrl_c,
|
||||
current_errors: args.current_errors,
|
||||
name: args.call_info.name_tag,
|
||||
raw_input: args.raw_input,
|
||||
},
|
||||
product,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Get the product of a list of numbers",
|
||||
example: "echo [2 3 3 4] | math product",
|
||||
result: Some(vec![UntaggedValue::int(72).into()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn to_byte(value: &Value) -> Option<Value> {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Int(num)) => Some(
|
||||
UntaggedValue::Primitive(Primitive::Filesize(convert_number_to_u64(&Number::Int(
|
||||
num.clone(),
|
||||
))))
|
||||
.into_untagged_value(),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate product of given values
|
||||
pub fn product(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
||||
let prod = reducer_for(Reduce::Product);
|
||||
|
||||
let first = values.get(0).ok_or_else(|| {
|
||||
ShellError::unexpected("Cannot perform aggregate math operation on empty data")
|
||||
})?;
|
||||
|
||||
match first {
|
||||
v if v.is_filesize() => to_byte(&prod(
|
||||
UntaggedValue::int(1).into_untagged_value(),
|
||||
values
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Filesize(num)),
|
||||
..
|
||||
} => UntaggedValue::int(*num as usize).into_untagged_value(),
|
||||
other => other.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)?)
|
||||
.ok_or_else(|| {
|
||||
ShellError::labeled_error(
|
||||
"could not convert to decimal",
|
||||
"could not convert to decimal",
|
||||
&name.span,
|
||||
)
|
||||
}),
|
||||
|
||||
v if v.is_none() => prod(
|
||||
UntaggedValue::int(1).into_untagged_value(),
|
||||
values
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||
..
|
||||
} => UntaggedValue::int(1).into_untagged_value(),
|
||||
other => other.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
_ => prod(UntaggedValue::int(1).into_untagged_value(), values.to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@ pub fn reducer_for(
|
||||
)),
|
||||
Reduce::Minimum => Box::new(|_, values| min(values)),
|
||||
Reduce::Maximum => Box::new(|_, values| max(values)),
|
||||
Reduce::Product => Box::new(|_, values| product(values)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +55,7 @@ pub enum Reduce {
|
||||
Summation,
|
||||
Minimum,
|
||||
Maximum,
|
||||
Product,
|
||||
Default,
|
||||
}
|
||||
|
||||
@ -133,3 +135,34 @@ pub fn min(data: Vec<Value>) -> Result<Value, ShellError> {
|
||||
tag: Tag::unknown(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn product(data: Vec<Value>) -> Result<Value, ShellError> {
|
||||
if data.is_empty() {
|
||||
return Err(ShellError::unexpected(ERR_EMPTY_DATA));
|
||||
}
|
||||
|
||||
let mut prod = UntaggedValue::int(1).into_untagged_value();
|
||||
for value in data {
|
||||
match value.value {
|
||||
UntaggedValue::Primitive(_) => {
|
||||
prod = match compute_values(Operator::Multiply, &prod, &value) {
|
||||
Ok(v) => v.into_untagged_value(),
|
||||
Err((left_type, right_type)) => {
|
||||
return Err(ShellError::coerce_error(
|
||||
left_type.spanned_unknown(),
|
||||
right_type.spanned_unknown(),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Attempted to compute the product of a value that cannot be multiplied.",
|
||||
"value appears here",
|
||||
value.tag.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(prod)
|
||||
}
|
||||
|
Reference in New Issue
Block a user