在 Rust 中编写定点函数 [英] Write fix point function in Rust

查看:55
本文介绍了在 Rust 中编写定点函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始 Rust 教程,并以使用递归的此类代码结束

I've just started Rust tutorial and ended with such code using recursion

extern crate rand;

use std::io;
use rand::Rng;
use std::cmp::Ordering;
use std::str::FromStr;
use std::fmt::{Display, Debug};

fn try_guess<T: Ord>(guess: T, actual: T) -> bool {
    match guess.cmp(&actual) {
        Ordering::Less => {
            println!("Too small");
            false
        }
        Ordering::Greater => {
            println!("Too big");
            false
        }
        Ordering::Equal => {
            println!("You win!");
            true
        }
    }
}

fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T)
    where <T as FromStr>::Err: Debug
{
    println!("PLease input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    let guess_int: T = guess.trim()
        .parse()
        .expect("Should enter integer number");

    println!("You guessed {} !", guess_int);

    if !try_guess(guess_int, actual) {
        guess_loop(actual)
    }
}

fn main() {
    println!("Guess the number!!!");

    let secret_number = rand::thread_rng().gen_range(1, 51);

    guess_loop(secret_number);

}

我希望从 guess_loop 函数中分解出递归并引入一个定点运算符:

I was hoping to factor-out the recursion from the guess_loop function and introduced a fix point operator:

fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T, recur: fn(T) -> ()) -> ()
    where <T as FromStr>::Err: Debug
{
    println!("PLease input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    let guess_int: T = guess.trim()
        .parse()
        .expect("Should enter integer number");

    println!("You guessed {} !", guess_int);

    if !try_guess(guess_int, actual) {
        recur(actual)
    }
}

fn fix<T, R>(func: fn(T, fn(T) -> R) -> R) -> fn(T) -> R {
    fn fixed(val: T) -> R {
        func(val, fixed)
    }
    fixed
}

fn main() {
    println!("Guess the number!!!");

    let secret_number = rand::thread_rng().gen_range(1, 51);

    fix(guess_loop)(secret_number);
}

但这导致了许多错误,例如

but this led to numerous errors, such as

error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
  --> src/main.rs:49:19
   |
49 |     fn fixed(val: T) -> R {
   |                   ^ use of type variable from outer function

error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
  --> src/main.rs:49:25
   |
49 |     fn fixed(val: T) -> R {
   |                         ^ use of type variable from outer function

error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } closure form instead
  --> src/main.rs:50:9
   |
50 |         func(val, fixed)
   |         ^^^^

我的下一次尝试是将 guess_loop 的定义更改为

My next attempt was changing guess_loop's definition to

fn guess_loop<T: Ord + FromStr + Display + Copy, F>(actual: T, recur: F) -> ()
where <T as FromStr>::Err: Debug,
      F: Fn(T) -> ()
{ ... }

并将 fix 重新定义为

and redefine fix as

fn fix<T, R, F>(func: fn(T, F) -> R) -> F
    where F: Fn(T) -> R
{
    let fixed = |val: T| func(val, fix(func));
    fixed
}

这导致了

error[E0308]: mismatched types
  --> src/main.rs:53:5
   |
53 |     fixed
   |     ^^^^^ expected type parameter, found closure
   |
   = note: expected type `F`
   = note:    found type `[closure@src/main.rs:52:17: 52:46 func:_]`

error: the type of this value must be known in this context
  --> src/main.rs:61:5
   |
61 |     fix(guess_loop)(secret_number);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

如何编写类似的 fix 函数?

How can I write a similar fix function?

推荐答案

首先,变量名在初始化后才存在.你不能让 fixed 像这样引用自己.

Firstly, variable names don't exist until after they're initialised. You can't have fixed refer to itself like that.

其次,您不能从函数中按值返回闭包,句点.泛型参数由调用者选择,调用者不知道函数内部的闭包类型是什么.

Secondly, you can't return closures by-value from a function, period. Generic parameters are chosen by the caller, and the caller has no idea what the type of a closure inside the function is going to be.

我并不是说下面的方法是最好的方法,但这是我能想到的最简单的类型检查方法.

I'm not claiming that what follows is the best way of doing this, but it was the simplest I was able to come up with that type-checks.

fn guess_loop<T>(actual: T, recur: &Fn(T)) -> ()
    where T: Ord + FromStr + Display + Copy,
          <T as FromStr>::Err: Debug
{
    // ...
}

fn fix<T, R, F>(func: F) -> Box<Fn(T) -> R>
    where T: 'static,
          R: 'static,
          F: Fn(T, &Fn(T) -> R) -> R + 'static
{
    use std::cell::RefCell;
    use std::rc::Rc;

    let fixed = Rc::new(RefCell::new(None));
    let fixed_fn = {
        let fixed = fixed.clone();
        move |val: T| -> R {
            let fixed_ref = fixed.borrow();
            let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
            func(val, &**fixed_ref)
        }
    };
    *fixed.borrow_mut() = Some(Box::new(fixed_fn));

    Box::new(move |val: T| -> R {
        let fixed_ref = fixed.borrow();
        let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
        fixed_ref(val)
    })
}

为了让 fixed_fn 引用自身,我们必须创建一些东西让它在它存在之前读取.不幸的是,这意味着有一个循环,而 Rust 讨厌 循环.因此,我们通过构造一个以 None 开头的引用计数 RefCell> 来实现这一点,并且稍后将对其进行变异以包含定点关闭.

In order for fixed_fn to refer to itself, we have to create something for it to read from before it exists. Unfortunately, this means having a cycle, and Rust hates cycles. So, we do this by constructing a reference-counted RefCell<Option<_>> that starts with None, and which will be mutated later to contain the fixed-point closure.

其次,我们不能将此句柄用作可调用对象,因此我们必须显式地拉出指向闭包的指针,以便将其传递给 func.

Secondly, we can't use this handle as a callable, so we have to explicitly pull a pointer to the closure out so that we can pass it to func.

第三,编译器似乎无法正确推断fixed 的类型.我希望它能够确定它是 Rc>>,但它拒绝这样做.结果,我们不得不求助于存储一个 BoxR>,因为我们不能明确地命名闭包的类型.

Third, the compiler doesn't seem to be able to infer the type of fixed correctly. I was hoping it would be able to work out that it is Rc<RefCell<Option<{closure}>>>, but it refused to do so. As a result, we have to resort to storing a Box<Fn(T) -> R>, since we can't name the type of the closure explicitly.

最后,我们必须构造一个 new 闭包,它接受 fixed 的第二个句柄,解包并调用它.同样,我们不能将 fixed 直接用作可调用对象.我们也不能重用闭包 inside fixed,因为要做到这一点,我们必须把它放在自己的 Rc 中,并且到那时,事情开始变得疯狂.

Finally, we have to construct a new closure that takes a second handle to fixed, unpacks it, and calls it. Again, we can't use fixed as a callable directly. We also can't re-use the closure inside fixed, because to do that we'd have to put that inside its own Rc and at that point, things are starting to get crazy.

... 更多疯狂.

最后,我们必须在 Box 中返回第二个闭包,因为正如我之前所说,我们不能按值返回闭包,因为我们无法在签名中命名它们的类型.

Finally, we have to return this second closure in a Box because, as I said before, we can't return closures by value because we can't name their types in the signature.

*深呼吸*

如果有人有更简单的解决方案,我很乐意看到.:P

If someone has a simpler solution, I'd love to see it. :P

这篇关于在 Rust 中编写定点函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆