Skip to content

Commit 8d631c5

Browse files
authored
macro for recursive closure (#74)
* saving progress * fix * different init style * actually I like this name * convert hld now * now test HLD on forest * saving progress * add sources before I forget * Update recursive_closure.rs * revert * docs are compiling now * add another example to docs * fix doc error * nits * remove unnecessary move * remove another unnecessary move * final touches * better style * one final nit * style --------- Co-authored-by: Luke Videckis <lukevideckis@gmail.com>
1 parent 00cc846 commit 8d631c5

File tree

4 files changed

+100
-3
lines changed

4 files changed

+100
-3
lines changed

examples/data_structures/seg_tree_yosupo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn main() {
2323

2424
let mut seg_tree = SegTree::<(u64, u64)>::build_on_array(
2525
&a,
26-
move |x, y| (x.0 * y.0 % MOD, (y.0 * x.1 + y.1) % MOD),
26+
|x, y| (x.0 * y.0 % MOD, (y.0 * x.1 + y.1) % MOD),
2727
(1, 0),
2828
);
2929

examples/graphs/hld_path_composite_yosupo.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ fn main() {
4141

4242
let mut st_forwards = SegTree::<(u64, u64)>::build_on_array(
4343
&input_a,
44-
move |x, y| (x.0 * y.0 % MOD, (y.0 * x.1 + y.1) % MOD),
44+
|x, y| (x.0 * y.0 % MOD, (y.0 * x.1 + y.1) % MOD),
4545
(1, 0),
4646
);
4747
let mut st_backwards = SegTree::<(u64, u64)>::build_on_array(
4848
&input_a,
49-
move |x, y| (x.0 * y.0 % MOD, (x.0 * y.1 + x.1) % MOD),
49+
|x, y| (x.0 * y.0 % MOD, (x.0 * y.1 + x.1) % MOD),
5050
(1, 0),
5151
);
5252

src/helpers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
//! # Helpers
22
pub mod compress;
3+
pub mod unsafe_recursive_closure;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//! # Unsafe Recursive Closure which can mutate vars in surrounding scope
2+
3+
/// - see <https://github.com/EgorKulikov/rust_algo/blob/master/algo_lib/src/misc/recursive_function.rs>
4+
/// - see <https://github.com/SrTobi/fix_fn>
5+
///
6+
/// # Example
7+
/// ```
8+
/// use programming_team_code_rust::unsafe_recursive_closure;
9+
///
10+
/// // move closures are okay
11+
/// let mut fib = unsafe_recursive_closure!(move |fib, i: u32| -> u32 {
12+
/// match i {
13+
/// 0 | 1 => i,
14+
/// _ => fib(i - 1) + fib(i - 2),
15+
/// }
16+
/// });
17+
/// assert_eq!(fib(7), 13);
18+
///
19+
/// // closures with no return type are okay
20+
/// let n = 5;
21+
/// let mut adj = vec![vec![]; n];
22+
/// for i in 1..n {
23+
/// adj[i / 2].push(i);
24+
/// adj[i].push(i / 2);
25+
/// }
26+
/// assert_eq!(2 * (n - 1), adj.iter().map(|elem| elem.len()).sum());
27+
/// let mut dfs = unsafe_recursive_closure!(|dfs, u: usize, p: Option<usize>| {
28+
/// adj[u].retain(|&v| Some(v) != p);
29+
/// for &v in &adj[u] {
30+
/// dfs(v, Some(u));
31+
/// }
32+
/// });
33+
/// dfs(0, None);
34+
/// assert_eq!(n - 1, adj.iter().map(|elem| elem.len()).sum());
35+
/// ```
36+
#[macro_export]
37+
macro_rules! unsafe_recursive_closure {
38+
(
39+
$($mov:ident)? |$self_arg:ident $(, $arg_name:ident : $arg_type:ty)* $(,)? | $(-> $ret_type:ty)?
40+
$body:block
41+
) => {{
42+
43+
trait HideFn {
44+
fn call(&mut self, $($arg_name : $arg_type ,)*) $(-> $ret_type)?;
45+
}
46+
47+
struct HideFnImpl<F: FnMut(&mut dyn HideFn, $($arg_type ,)*) $(-> $ret_type)?>(
48+
std::cell::UnsafeCell<F>,
49+
);
50+
51+
impl<F> HideFnImpl<F>
52+
where
53+
F: FnMut(&mut dyn HideFn, $($arg_type ,)*) $(-> $ret_type)?,
54+
{
55+
fn new(f: F) -> Self {
56+
Self {
57+
0: std::cell::UnsafeCell::new(f),
58+
}
59+
}
60+
}
61+
62+
impl<F: FnMut(&mut dyn HideFn, $($arg_type ,)*) $(-> $ret_type)?> HideFn for HideFnImpl<F> {
63+
#[inline]
64+
fn call(&mut self, $($arg_name : $arg_type ,)*) $(-> $ret_type)? {
65+
unsafe { (*self.0.get())(self, $($arg_name ,)*) }
66+
}
67+
}
68+
69+
let mut inner = HideFnImpl::new(
70+
#[inline]
71+
$($mov)? |$self_arg, $($arg_name : $arg_type ,)*| $(-> $ret_type)? {
72+
let mut $self_arg = |$($arg_name : $arg_type ),*| $self_arg.call($($arg_name ,)*);
73+
{
74+
$body
75+
}
76+
}
77+
);
78+
79+
#[inline]
80+
move |$($arg_name : $arg_type),*| $(-> $ret_type)? {
81+
inner.call($($arg_name),*)
82+
}
83+
}};
84+
(
85+
$($mov:ident)? |$self_arg:ident : $self_type:ty $(, $arg_name:ident $(: $arg_type:ty)?)* $(,)? | $(-> $ret_type:ty)?
86+
$body:block
87+
) => {
88+
compile_error!(concat!("First parameter ", stringify!($self_arg), " may not have type annotation!"));
89+
};
90+
(
91+
$($mov:ident)? |$self_arg:ident $(, $arg_name:ident $(: $arg_type:ty)?)* $(,)? | $(-> $ret_type:ty)?
92+
$body:block
93+
) => {
94+
compile_error!("All parameters except first need to have an explicit type annotation!");
95+
};
96+
}

0 commit comments

Comments
 (0)