1use crate::matrix::MatrixValue;
2use crate::matrix::value::value_to_f64;
3use ndarray::ArrayD;
4use nu_engine::ClosureEvalOnce;
5use nu_engine::command_prelude::*;
6use nu_protocol::engine::Closure;
7
8#[derive(Clone)]
9pub struct MatrixMap;
10
11impl Command for MatrixMap {
12 fn name(&self) -> &str {
13 "matrix map"
14 }
15
16 fn signature(&self) -> Signature {
17 Signature::build("matrix map")
18 .input_output_types(vec![(
19 Type::Custom("matrix".into()),
20 Type::Custom("matrix".into()),
21 )])
22 .required(
23 "closure",
24 SyntaxShape::Closure(Some(vec![SyntaxShape::Number])),
25 "The closure to apply to each element.",
26 )
27 .category(Category::Filters)
28 }
29
30 fn description(&self) -> &str {
31 "Apply a closure to each element of a matrix and return a new matrix."
32 }
33
34 fn search_terms(&self) -> Vec<&str> {
35 vec!["each", "apply", "element"]
36 }
37
38 fn run(
39 &self,
40 engine_state: &EngineState,
41 stack: &mut Stack,
42 call: &Call,
43 input: PipelineData,
44 ) -> Result<PipelineData, ShellError> {
45 let head = call.head;
46 let closure: Closure = call.req(engine_state, stack, 0)?;
47 let matrix = MatrixValue::from_value(&input.into_value(head)?)?;
48
49 let shape = matrix.array.shape().to_vec();
50 let mut vals = Vec::with_capacity(matrix.array.len());
51 for &val in matrix.array.iter() {
52 let element = Value::float(val, head);
53 let result = ClosureEvalOnce::new(engine_state, stack, closure.clone())
54 .run_with_value(element)?
55 .into_value(head)?;
56 vals.push(value_to_f64(&result, head).map_err(|_| {
57 ShellError::Generic(nu_protocol::shell_error::generic::GenericError::new(
58 "Invalid result",
59 "closure must return a number",
60 head,
61 ))
62 })?);
63 }
64
65 let array = ArrayD::from_shape_vec(shape, vals).map_err(|e| {
66 ShellError::Generic(nu_protocol::shell_error::generic::GenericError::new(
67 "Reshape error",
68 e.to_string(),
69 head,
70 ))
71 })?;
72
73 Ok(MatrixValue::new(array)
74 .into_value(head)
75 .into_pipeline_data())
76 }
77
78 fn examples(&self) -> Vec<Example<'static>> {
79 vec![
80 Example {
81 description: "Double each element in a matrix",
82 example: "[[1 2] [3 4]] | into matrix | matrix map {|e| $e * 2} | matrix into-nu | to nuon",
83 result: Some(Value::test_string("[[2.0, 4.0], [6.0, 8.0]]")),
84 },
85 Example {
86 description: "Add 10 to each element of an identity matrix",
87 example: "matrix identity 2 | matrix map {|e| $e + 10} | matrix into-nu | to nuon",
88 result: Some(Value::test_string("[[11.0, 10.0], [10.0, 11.0]]")),
89 },
90 ]
91 }
92}
93
94#[cfg(test)]
95mod test {
96 use super::*;
97
98 #[test]
99 fn test_examples() -> nu_test_support::Result {
100 nu_test_support::test().examples(MatrixMap)
101 }
102}