bertiqwerty / exmex

Math parser and evaluator in Rust, capable of partial differentiation, allows the use of custom operators.
Apache License 2.0
39 stars 6 forks source link

Convinient owned version of FlatEx (with variable names) #17

Closed saona-raimundo closed 3 years ago

saona-raimundo commented 3 years ago

Hi again!

Thanks for the work for #1! I have no complaints, but wanted to ask for a convenience struct for ease of use of the crate.

Request

Add a struct that wraps over FlatEx allowing for:

Use-case

The idea is simple: an online plotter (see website). In my application, the following steps are performed:

In the last step, I serialize all the input and deserialize upon a new visit.

Work-around

I am serializing and deserializing the input and parsing when reconstructing the struct. Hope this code is helpful.

struct FnInput {
    raw_input: String,
    exp: FlatEx<f64>, // Exmex version 0.7.1
}

impl FnInput {
    pub fn eval(&self, vars: &[f64]) -> Result<f64, ExParseError> {
        self.exp(vars)
    }
}

impl Serialize for FnInput {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut s = serializer.serialize_struct("FnInput", 1)?;
        s.serialize_field("raw_input", &self.raw_input)?;
        s.end() // We do not serialize FlatEx
    }
}

impl<'de> Deserialize<'de> for FnInput {
    fn deserialize<D>(deserializer: D) -> Result<FnInput, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Debug, Clone, Deserialize)]
        struct __FnInput {
            raw_input: String,
        }
        let __fn_input = __FnInput::deserialize(deserializer)?;
        let exp = exmex::parse_with_default_ops::<f64>(&__fn_input.raw_input).unwrap(); // Reparse here 
        Ok(FnInput {
            string: __fn_input.string,
            kind,
        })
    }
}

Note: Another option is to parse for every evaluation, but that is too wasteful...

Details

I presume this would entail a version of FlatEx with no life parameter, but you know better. Feel free to deny this request if it feels out of the scope of the crate for you.

Either way, I do not how to reconstruct how to easily reconstruct my workaround with exmex version 0.9.

bertiqwerty commented 3 years ago

Interesting. I will think about how to add an OwningFlatEx or the like.

However, I do not understand why your workaround works for v0.7.1 but not for v0.9.*. You should be able to do the same thing. Or do you mean you do not know how to make use of FlatEx's serde functionality in v0.9.2 to simplify your workaround? In the current implementation to my understanding you need some place that keeps the ownership of the raw string. The serde-doc says about the used visit_borrowed_str in the FlatEx deserialization method the following.

The input contains a string that lives at least as long as the Deserializer. This enables zero-copy deserialization of strings in some formats. For example JSON input containing the JSON string "borrowed" can be deserialized with zero copying into a &'a str as long as the input data outlives 'a.

The default implementation forwards to visit_str.

saona-raimundo commented 3 years ago

Interesting. I will think about how to add an OwningFlatEx or the like.

Thank you!

However, I do not understand why your workaround works for v0.7.1 but not for v0.9.*. You should be able to do the same thing. Or do you mean you do not know how to make use of FlatEx's serde functionality in v0.9.2 to simplify your workaround?

At the end of the day, it is really more of a "can not easily translate for v0.9" than an impossibility (edited the comment). Having a lifetime in FlatEx means having one in the wrapper too. But this is really an imaginary lifetime because all the data is owned. So yes, the workaround can be used in v0.9, but needs a more complex solution.

A workaround that would be in line with v0.9 would look like this:

struct FnInput<'a> {
    raw_input: &'a str,
    exp: FlatEx<'a, f64>,
}

where raw_input is stored to circumvent the behaviour of unparse while recovering the original string.

bertiqwerty commented 3 years ago

Ah. You are right. In v0.7.1, FlatEx was owning its strings. I think, we can create an OwningFlatEx that is more efficient than the one from v0.7.1.

bertiqwerty commented 3 years ago

Hi @saona-raimundo, does

https://docs.rs/exmex/0.9.3/exmex/#owned-expression

help you? If not, feel free to re-open this issue.

saona-raimundo commented 3 years ago

It looks like what I needed, thanks!!

Could you implement the basic traits for OwnedFlatEx too, please? Like Clone, Debug, Eq and Ord, just as it is for FlatEx? :)

bertiqwerty commented 3 years ago

Ah. Yes. Sure. I forgot those.

bertiqwerty commented 3 years ago

I added this to version 0.9.4.

saona-raimundo commented 3 years ago

Thank you so much! It works like a charm :D