Mercurial > lbo > hg > memoize
changeset 3:0bc33bd08f39
Add README
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Thu, 15 Oct 2020 13:28:15 +0200 |
parents | 112b5335a4a0 |
children | 702afb217145 |
files | README.md examples/test1.rs src/lib.rs |
diffstat | 3 files changed, 62 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Thu Oct 15 13:28:15 2020 +0200 @@ -0,0 +1,8 @@ +# memoize + +A `#[memoize]` attribute for somewhat simple Rust functions. That's it. + +Read the documentation (`cargo doc --open`) for the sparse details, or take a +look at the `examples/`, if you want to know more. + +Intentionally not yet on crates.rs.
--- a/examples/test1.rs Thu Oct 15 12:53:52 2020 +0200 +++ b/examples/test1.rs Thu Oct 15 13:28:15 2020 +0200 @@ -1,12 +1,21 @@ use memoize::memoize; +#[derive(Debug, Clone)] +struct ComplexStruct { + s: String, + b: bool, + i: i32, +} + #[memoize] -fn hello(a: i32) -> bool { - println!("HELLO"); - a%2 == 0 +fn hello(key: String) -> ComplexStruct { + println!("hello: {}", key); + ComplexStruct { s: key, b: false, i: 332 } } fn main() { - println!("result: {}", hello(32)); - println!("result: {}", hello(32)); + println!("result: {:?}", hello("ABC".to_string())); + println!("result: {:?}", hello("DEF".to_string())); + println!("result: {:?}", hello("ABC".to_string())); + println!("result: {:?}", memoized_original_hello("ABC".to_string())); }
--- a/src/lib.rs Thu Oct 15 12:53:52 2020 +0200 +++ b/src/lib.rs Thu Oct 15 13:28:15 2020 +0200 @@ -12,12 +12,38 @@ /* * TODO: - * - Create static map for memoized arguments/results - * - Create memoized version of function - * - Rename original function to memoized_original_{fn} - * + * - Functions with multiple arguments. */ +/** + * memoize is an attribute to create a memoized version of a (simple enough) function. + * + * So far, it works on functions with one argument which is Clone-able, returning a Clone-able + * value. + * + * Calls are memoized for the lifetime of a program, using a statically allocated, Mutex-protected + * HashMap. + * + * Memoizing functions is very simple: As long as the above-stated requirements are fulfilled, + * simply use the `#[memoize::memoize]` attribute: + * + * ``` + * use memoize::memoize; + * #[memoize] + * fn hello(arg: String) -> bool { + * arg.len()%2 == 0 + * } + * + * // `hello` is only called once. + * assert!(! hello("World".to_string())); + * assert!(! hello("World".to_string())); + * ``` + * + * If you need to use the un-memoized function, it is always available as `memoized_original_{fn}`, + * in this case: `memoized_original_hello()`. + * + * See the `examples` for concrete applications. + */ #[proc_macro_attribute] pub fn memoize(_attr: TokenStream, item: TokenStream) -> TokenStream { let func = parse_macro_input!(item as ItemFn); @@ -26,13 +52,13 @@ let fn_name = &func.sig.ident.to_string(); let renamed_name = format!("memoized_original_{}", fn_name); let map_name = format!("memoized_mapping_{}", fn_name); - println!("{}", fn_name); let mut type_in = None; let mut name_in = None; let type_out; // Only one argument + // TODO: cache multiple arguments if sig.inputs.len() == 1 { if let syn::FnArg::Typed(ref arg) = sig.inputs[0] { type_in = Some(arg.ty.clone()); @@ -56,34 +82,33 @@ ); } } - // TODO: Cache methods too match &sig.output { syn::ReturnType::Default => type_out = quote::quote! { () }, syn::ReturnType::Type(_, ty) => type_out = ty.to_token_stream(), } let type_in = type_in.unwrap(); - let map_ident = syn::Ident::new(&map_name.to_uppercase(), sig.span()); + let name_in = name_in.unwrap(); + let store_ident = syn::Ident::new(&map_name.to_uppercase(), sig.span()); let store = quote::quote! { lazy_static::lazy_static! { - static ref #map_ident : std::sync::Mutex<std::collections::HashMap<#type_in, #type_out>> = + static ref #store_ident : std::sync::Mutex<std::collections::HashMap<#type_in, #type_out>> = std::sync::Mutex::new(std::collections::HashMap::new()); } }; let mut renamed_fn = func.clone(); renamed_fn.sig.ident = syn::Ident::new(&renamed_name, func.sig.span()); + let memoized_id = &renamed_fn.sig.ident; - let name_in = name_in.unwrap(); - let memoized_id = &renamed_fn.sig.ident; let memoizer = quote::quote! { #sig { - let mut hm = &mut #map_ident.lock().unwrap(); + let mut hm = &mut #store_ident.lock().unwrap(); if let Some(r) = hm.get(&#name_in) { - return *r; + return r.clone(); } - let r = #memoized_id(#name_in); - hm.insert(#name_in, r); + let r = #memoized_id(#name_in.clone()); + hm.insert(#name_in, r.clone()); r } }; @@ -103,5 +128,4 @@ } #[cfg(test)] -mod tests { -} +mod tests {}