我正在玩lambda演算,但由于我不太精通泛型,我想确认我对某些事情的理解并解决错误。请考虑以下定义:
pub trait Term {} // a lambda term - a variable, an abstraction or an application
pub struct Var(pub usize); // a variable, De Bruijn style
pub struct Abs<T: Term>(T); // an abstraction
pub struct App<T: Term, U: Term>(T, U); // application of T on U
我理解(即,否则它不起作用)我需要App
在<T: Term, U: Term>
上是通用的,而不仅仅是<T: Term>
能够例如Var
将App
应用于App(Var(x), App(...))
,即拥有Term
。
前面提到的结构都是impl Term for Var {}
impl<T: Term> Term for Abs<T> {}
impl<T: Term> Term for App<T, T> {}
s:
App<T, U>
有趣的是我在这里不需要fmt::Display
,但希望到目前为止这么好 - 现在我想为前面提到的结构实现use std::fmt;
use std::fmt::Display;
impl fmt::Display for Var {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T: Term+Display> Display for Abs<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "λ{}", self.0)
}
}
:
impl
这两个impl<T: Term+Display> Display for App<T, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
App(Var(_), Var(_)) => write!(f, "{}{}", self.0, self.1),
_ => unimplemented!()
}
}
}
工作正常;我包括他们,因为下一个依赖他们:
error[E0308]: mismatched types
--> src\ast.rs:34:8
|
34 | App(Var(_), Var(_)) => write!(f, "{}{}", self.0, self.1),
| ^^^^^^ expected type parameter, found struct `ast::Var`
|
= note: expected type `T`
= note: found type `ast::Var`
但它失败了:
App
我想根据内容的类型以不同的方式打印def ben_loss(x, x_hat):
B0 = tf_median(tf.transpose(x_hat))
F0 = tf.abs(x_hat - B0) + 1e-10
sigma = tf.reduce_mean(tf.sqrt( / 0.4), axis=0)
background_term = tf.reduce_mean(F0 / sigma, axis=-1)
bce = binary_crossentropy(x, x_hat)
loss = bce + background_term
return loss
。我试图找到一个相关的问题,但它们主要围绕相关类型。有一个简单的解决方案还是我必须重新定义这些定义?
答案 0 :(得分:2)
我想根据内容的类型以不同的方式打印
App
。
这很简单,只需为您希望能够打印的每种独特类型的Display
实施App
:
use std::fmt;
struct App<T, U>(T, U);
impl fmt::Display for App<i32, bool> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Awesome choice!")
}
}
impl fmt::Display for App<bool, String> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Woah, a string? Really?")
}
}
fn main() {
println!("{}", App(42i32, false));
println!("{}", App(true, "wow".to_string()));
}
您还可以接受具有其自己的泛型的其他类型:
impl<T> fmt::Display for App<Vec<T>, char> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} of them; {}", self.0.len(), self.1)
}
}
请注意,这没有unimplemented!
电话;在编译时检查所有内容:
error[E0277]: the trait bound `App<bool, bool>: std::fmt::Display` is not satisfied
--> src/main.rs:24:24
|
24 | println!("{}", App(true, false));
| ^^^^^^^^^^^^^^^^ the trait `std::fmt::Display` is not implemented for `App<bool, bool>`
|
= note: `App<bool, bool>` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
= note: required by `std::fmt::Display::fmt`
标准库中有一个非常类似的模拟:Cursor
。此类型接受通用,但所有有趣的功能仅针对少数具体类型实现,如&[u8]
或Vec<u8>
。
但它仍会造成麻烦,例如使用
App<Var, App>
,因为内部App
再次需要2个类型参数
是的,您必须指定泛型,因为App
不是类型,它只是一个踩踏石头。
这一切都取决于你想做什么。最简单的是让App
由两种类型组成,只要你不使用它们:
impl<T, U> fmt::Display for App<i32, App<T, U>> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Semi-awesome")
}
}
如果您希望能够显示App
,则需要限制泛型,以便显示App
:
impl<T, U> fmt::Display for App<i32, App<T, U>>
where App<T, U>: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}) squared!", self.1)
}
}
用
查看fn main() {
let a = App(42i32, false);
println!("{}", a);
let b = App(100, a);
println!("{}", b);
let c = App(100, b);
println!("{}", c);
}
我猜测后续问题将是针对所有非特殊条件的某种回退或默认情况。类似的东西:
impl fmt::Display for App<i32, bool> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Awesome choice!")
}
}
impl<T: fmt::Display, U: fmt::Display> fmt::Display for App<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Boring: {} + {}", self.0, self.1)
}
}
不,那不会编译! i32
和bool
也会实施Display
,因此要选择的实现方式不明确。此时,您进入了specialization的领域。这迫使您真正理解孤儿规则。
据我了解当前的专业化实现,你不能专注于具体的类型,只能在特征上。