diff --git a/src/examples.rs b/src/examples.rs index a3c1998..7784dff 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -10,8 +10,6 @@ use crate::scratch; fn cube_thing() -> Rule { - let mesh = prim::cube(); - // Quarter-turn in radians: let qtr = std::f32::consts::FRAC_PI_2; @@ -28,24 +26,42 @@ fn cube_thing() -> Rule { geometry::Rotation3::from_axis_angle(z, -qtr).to_homogeneous(), ]; - let rec = || -> RuleEval { + let rec = move |self_: Rc| -> RuleEval { let gen_rulestep = |rot: &Mat4| -> Child { let m: Mat4 = rot * Matrix4::new_scaling(0.5) * geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous(); Child { - rule: Rule { eval: Box::new(rec) }, + rule: self_.clone(), xf: m, vmap: vec![], } }; RuleEval { - geom: mesh.clone(), + geom: prim::cube(), final_geom: prim::empty_mesh(), children: turns.iter().map(gen_rulestep).collect(), } }; + // I can't really do *mutual* recursion with the above, can I? I'd + // need actual functions for that. + + // Also: 'turns' above is a problem. I can't clone it *inside* the + // closure because it doesn't live long enough (it is out of scope + // when the closure runs). I can't move it, or Fn becomes + // FnOnce. It doesn't implement Copy, and really can't, because it + // has vectors inside. + // + // (I guess I could use Rc instead if I want cheap + // support for Copy. Or... just put prim::cube() right inside.) + // + // what did I learn from this? That "constants" outside the + // closure only work the way I think they should work if: + // - they're actually static + // - they implement Copy + // - the closure can move them + Rule { eval: Box::new(rec) } } @@ -492,7 +508,16 @@ pub fn main() { println!("Writing {}...", fname); mesh.write_stl_file(&fname).unwrap(); } - */ + */ + + fn run_test_iter(r: &Rc, iters: usize, name: &str) { + println!("Running {}...", name); + let (mesh, nodes) = Rule::to_mesh_iter(r.clone(), iters); + println!("Evaluated {} rules", nodes); + let fname = format!("{}.stl", name); + println!("Writing {}...", fname); + mesh.write_stl_file(&fname).unwrap(); + } /* run_test(CubeThing::init(), Rule { eval: CubeThing::rec }, 3, "cube_thing"); @@ -516,6 +541,11 @@ pub fn main() { // let f = 20; // run_test_iter(Twist::init(f as f32, 32), 100*f, "twist2"); + let rule = Rc::new(cube_thing()); + + run_test_iter(&rule, 3, "cube_thing3"); + + if false { let a = vec![1,2,3]; diff --git a/src/rule.rs b/src/rule.rs index 397961c..3d3fef3 100644 --- a/src/rule.rs +++ b/src/rule.rs @@ -1,5 +1,6 @@ use crate::openmesh::{OpenMesh, Tag, Mat4}; //use crate::prim; +use std::rc::Rc; /// Definition of a rule. In general, a `Rule`: /// @@ -7,7 +8,7 @@ use crate::openmesh::{OpenMesh, Tag, Mat4}; /// - tells what other rules to invoke, and what to do with their /// geometry pub struct Rule { - pub eval: Box RuleEval>, + pub eval: Box) -> RuleEval>, } // TODO: It may be possible to have just a 'static' rule that requires // no function call. @@ -51,7 +52,7 @@ pub struct RuleEval { pub struct Child { /// Rule to evaluate to produce geometry - pub rule: Rule, + pub rule: Rc, /// The transform to apply to all geometry produced by `rule` /// (including its own `geom` and `final_geom` if needed, as well @@ -79,11 +80,11 @@ impl Rule { /// Convert this `Rule` to mesh data, recursively (depth first). /// `iters_left` sets the maximum recursion depth. This returns /// (geometry, number of rule evaluations). - pub fn to_mesh(&self, iters_left: u32) -> (OpenMesh, usize) { + pub fn to_mesh(s: Rc, iters_left: u32) -> (OpenMesh, usize) { let mut evals = 1; - let rs: RuleEval = (self.eval)(); + let rs: RuleEval = (s.eval)(s.clone()); if iters_left <= 0 { return (rs.final_geom, 1); // TODO: This is probably wrong because of the way that @@ -96,7 +97,7 @@ impl Rule { let subgeom: Vec<(OpenMesh, &Vec)> = rs.children.iter().map(|sub| { // Get sub-geometry (still un-transformed): - let (submesh, eval) = sub.rule.to_mesh(iters_left - 1); + let (submesh, eval) = Rule::to_mesh(sub.rule.clone(), iters_left - 1); // Tally up eval count: evals += eval; @@ -112,7 +113,7 @@ impl Rule { /// This should be identical to to_mesh, but implemented /// iteratively with an explicit stack rather than with recursive /// function calls. - pub fn to_mesh_iter(&self, max_depth: usize) -> (OpenMesh, usize) { + pub fn to_mesh_iter(s: Rc, max_depth: usize) -> (OpenMesh, usize) { struct State { // The set of rules we're currently handling: @@ -133,7 +134,7 @@ impl Rule { // (usually because they involve multiple rules). // // We evaluate our own rule to initialize the stack: - let eval = (self.eval)(); + let eval = (s.eval)(s.clone()); let mut stack: Vec = vec![State { rules: eval.children, next: 0, @@ -162,7 +163,7 @@ impl Rule { // Evaluate the rule: let child = &s.rules[s.next]; - let mut eval = (child.rule.eval)(); + let mut eval = (child.rule.eval)(child.rule.clone()); eval_count += 1; // Make an updated world transform: diff --git a/src/scratch.rs b/src/scratch.rs index 66c544e..be5407f 100644 --- a/src/scratch.rs +++ b/src/scratch.rs @@ -118,3 +118,180 @@ impl Foo5 { } } +// This is valid and I can recurse: +struct W { + b: Box W>, +} +struct Foo6 {} +impl Foo6 { + fn fn1(s: &Rc) -> W { + let s2 = Rc::clone(&s); + W { b: Box::new(move || Self::fn1(&s2)) } + } + fn fn2(s: &Rc) -> (W, W) { + let s2 = Rc::clone(&s); + let w2 = W { b: Box::new(move || Self::fn1(&s2)) }; + let s3 = Rc::clone(&s); + let w3 = W { b: Box::new(move || Self::fn1(&s3)) }; + (w2, w3) + } +} + +fn foo6() { + + // Whatever (note that it doesn't automatically do Copy): + struct State { + v: u32, + } + + // Purposely put state somewhere it goes out of scope: + let s = { + let s_orig = State { + v: 105, + }; + Rc::new(s_orig) + }; + /* + let fn1 = |f: &dyn Fn(&dyn Fn() -> W) -> (&dyn Fn() -> W)| -> (&dyn Fn() -> W) { + &(|| -> W { + let s2 = Rc::clone(&s); + W { b: Box::new(move || f(f)) } + }) + }; + + let f2 = fn1(fn1); + */ +} + +fn foo7(t: impl Clone) -> impl Clone { + t.clone() +} + +fn foo7b(t: T) -> T { + t.clone() +} + +fn foo7c(t: T) -> T where T: Clone { + t.clone() +} + +// A simple implementation of the Y Combinator +// λf.(λx.xx)(λx.f(xx)) +// <=> λf.(λx.f(xx))(λx.f(xx)) + +// CREDITS: A better version of the previous code that was posted here, with detailed explanation. +// See and also . + +// A function type that takes its own type as an input is an infinite recursive type. +// We introduce a trait that will allow us to have an input with the same type as self, and break the recursion. +// The input is going to be a trait object that implements the desired function in the interface. +// NOTE: We will be coercing a reference to a closure into this trait object. + +trait Apply { + fn apply(&self, f: &dyn Apply, t: T) -> R; +} + +// In Rust, closures fall into three kinds: FnOnce, FnMut and Fn. +// FnOnce assumed to be able to be called just once if it is not Clone. It is impossible to +// write recursive FnOnce that is not Clone. +// All FnMut are also FnOnce, although you can call them multiple times, they are not allow to +// have a reference to themselves. So it is also not possible to write recursive FnMut closures +// that is not Clone. +// All Fn are also FnMut, and all closures of Fn are also Clone. However, programmers can create +// Fn objects that are not Clone + +// This will work for all Fn objects, not just closures +// And it is a little bit more efficient for Fn closures as it do not clone itself. +impl Apply for F where F: + Fn(&dyn Apply, T) -> R +{ + fn apply(&self, f: &dyn Apply, t: T) -> R { + self(f, t) + + // NOTE: Each letter is an individual symbol. + // (λx.(λy.xxy))(λx.(λy.f(λz.xxz)y))t + // => (λx.xx)(λx.f(xx))t + // => (Yf)t + } +} + +// This works for all closures that is Clone, and those are Fn. +// impl Apply for F where F: FnOnce( &Apply, T ) -> R + Clone { +// fn apply( &self, f: &Apply, t: T ) -> R { +// (self.clone())( f, t ) + +// // If we were to pass in self as f, we get - +// // NOTE: Each letter is an individual symbol. +// // λf.λt.sft +// // => λs.λt.sst [s/f] +// // => λs.ss +// } +// } + +// Before 1.26 we have some limitations and so we need some workarounds. But now impl Trait is stable and we can +// write the following: + +fn y(f:impl Fn(&dyn Fn(T) -> R, T) -> R) -> impl Fn(T) -> R { + move |t| ( + |x: &dyn Apply, y| x.apply(x, y) + ) ( + &|x: &dyn Apply, y| f( + &|z| x.apply(x,z), + y + ), + t + ) +} + +// fn y(f:impl FnOnce(&Fn(T) -> R, T) -> R + Clone) -> impl FnOnce(T) -> R { +// |t| (|x: &Apply,y| x.apply(x,y)) +// (&move |x:&Apply,y| f(&|z| x.apply(x,z), y), t) + +// // NOTE: Each letter is an individual symbol. +// // (λx.(λy.xxy))(λx.(λy.f(λz.xxz)y))t +// // => (λx.xx)(λx.f(xx))t +// // => (Yf)t +// } + +// Previous version removed as they are just hacks when impl Trait is not available. + +fn fac(n: usize) -> usize { + let almost_fac = |f: &dyn Fn(usize) -> usize, x| + if x == 0 { + 1 + } else { + x * f(x - 1) + } + ; + let fac = y( almost_fac ); + fac(n) +} + +fn fib( n: usize ) -> usize { + let almost_fib = |f: &dyn Fn(usize) -> usize, x| + if x < 2 { + 1 + } else { + f(x - 2) + f(x - 1) + }; + let fib = y(almost_fib); + fib(n) +} + +fn optimal_fib( n: usize ) -> usize { + let almost_fib = |f: &dyn Fn((usize,usize,usize)) -> usize, (i0,i1,x)| + match x { + 0 => i0, + 1 => i1, + x => f((i1,i0+i1, x-1)) + } + ; + let fib = |x| y(almost_fib)((1,1,x)); + fib(n) +} + +fn test_y() { + println!("{}", fac(10)); + println!("{}", fib(10)); + println!("{}", optimal_fib(10)); +}