我正在编写一些代码,我对延迟评估的函数链感兴趣。换句话说,它存储您想要的所有操作,并且只对它们进行一起评估。
当链中的所有函数都采用相同的类型并返回相同的类型时,这非常容易。然而,当函数链每次返回不同的类型时,我一直困惑于如何使其工作。这个简单的例子可以在下面的代码中看到:
struct FuncChain<T> {
funcs: Vec<fn(T) -> T>
}
impl<T> FuncChain<T> {
fn call(&self, input: T) -> T {
self.funcs.iter().fold(input, |prev, func| func(prev))
}
}
fn main(){
let fc = FuncChain {
funcs: vec![
|x| x + 1,
|x| x + 2,
|x| x * 2,
|x| x - 2,
]
};
println!("{}", fc.call(1));
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
所以在这种情况下我们就去i32 -> i32 -> i32 -> i32 -> i32。
我想做的是一个更一般的情况,我们去A -> B -> C -> D -> E,这意味着funcs向量包含:fn(A) -> B、fn(B) -> C、fn(C) -> D和fn(D) -> E。但是如何将此类型定义分配给结构体呢?我无法创建具有异构类型的向量,即使可以,该结构的类型签名是什么?
我也许可以进行递归类型定义,其中保存FuncChain指向第一个函数对象的指针,以及链中的下一个对象:
struct FuncChain<S, T, U> {
func: fn(S) -> T,
next: FuncChain<T, U, ?>
}
impl<S, T, U> FuncChain<S, T, U> {
fn call(&self, input: T) -> T {
self.funcs.iter().fold(input, |prev, func| func(prev))
}
}
fn main(){
let fc = FuncChain {
funcs: vec![
|x| x.toString(),
|x| u8::fromStr(x),
|x| x.toString(),
|x| i32::fromStr(x),
]
};
println!("{}", fc.call(1));
}
Run Code Online (Sandbox Code Playgroud)
但是,这当然行不通,因为我不知道 的输出类型next。
如何才能做到这一点?
您的问题类似于Iterator,因此可以解决相同的解决方案:指示“可调用”的特征。
该特征使您可以通过让结构将其表示为“无论做什么”来“打破”当前基于结构的系统的无限递归。
struct Chain<U, V, F> {
prev: F,
f: fn(U) -> V,
}
trait FuncChain<T, U> {
fn call(&self, _: T) -> U;
fn chain<V>(self, next: fn(U) -> V) -> Chain<U, V, Self>
where
Self: Sized,
{
Chain {
prev: self,
f: next,
}
}
}
impl<T, U> FuncChain<T, U> for fn(T) -> U {
fn call(&self, t: T) -> U {
self(t)
}
}
impl<T, U, V, F> FuncChain<T, V> for Chain<U, V, F>
where
F: FuncChain<T, U>,
{
fn call(&self, t: T) -> V {
(self.f)(self.prev.call(t))
}
}
fn main() {
let c = ((|x| x + 1) as fn(i32) -> i32)
.chain(|x| x * 2)
.chain(|x| x - 2);
println!("{}", c.call(5));
}
Run Code Online (Sandbox Code Playgroud)
更好的 Rustacean 可能可以设计一种更简单的方法来实现这一目标。
如果您愿意使用 nightly,那么可能有一种方法可以使用Fn而不需要自定义特征。
天啊,从根本上来说,这只是.让你可以只用一个通用函数和一个闭包来管理,我必须检查一下。
| 归档时间: |
|
| 查看次数: |
270 次 |
| 最近记录: |