如何在不传递变量或不使用全局变量的情况下使变量可用于函数,或者应该采取不同的方法



我正在编写一个函数来修复rust(以及其他语言(中的浮点精度问题。我需要根据传递的参数在函数中选择.floor().ceil()。解决这个问题的最佳方法是什么。如果某个功能之外的其他功能更好地服务于提议,那么无论如何!谢谢你的帮助!

fn main() {
to_round(7.987, "floor");
}
fn to_round(n: f64, floor_or_ceil: &str) -> f64 {
fn test(diff_n: f64) -> f64 {
if floor_or_ceil == "floor" {
diff_n.floor()
} else {
diff_n.ceil()
}
}
test(n)
}

如果用闭包替换内部fn函数,那么您所拥有的代码将被编译。闭包可以引用封闭作用域中的变量,而fns不能。

fn main() {
to_round(7.987, "floor");
}
fn to_round(n: f64, floor_or_ceil: &str) -> f64 {
let test = |diff_n: f64| -> f64 {
if floor_or_ceil == "floor" {
diff_n.floor()
} else {
diff_n.ceil()
}
};
test(n)
}

然而,关闭确实有一些注意事项;特别是,它们不能是泛型函数,也不能在函数指针所在的地方使用(除非它们没有提到("close-over"(封闭范围中的任何变量,并且仅使用闭包语法而不是fn语法(。

这类问题的最通用的干净解决方案是显式地传递所需的参数,您可以通过使用结构(尤其是在需要多个值的情况下(和使该结构的函数方法成为来最大限度地减少它的混乱

fn main() {
let c = MathContext { rounding_mode: "floor" };
c.to_round(7.987);
}
struct MathContext {
rounding_mode: &'static str,  // Side note: this should really be an enum, not a string
// And you can add more fields here for any other parameters needed.
}
impl MathContext {
fn to_round(&self, n: f64) -> f64 {
self.test(n)
}
fn test(&self, diff_n: f64) -> f64 {
if self.rounding_mode == "floor" {
diff_n.floor()
} else {
diff_n.ceil()
}
}
}

这套方法是否适用取决于你实际在做什么;如果CCD_ 6对CCD_。但这种模式似乎在代码的其他地方很有用,至少如果你正在做一些事情,比如选择哪种方式来舍入数字。

首先,应该取消使用&strs作为枚举。只需声明一个枚举:

#[derive(Clone, Copy)]
enum Round {
Floor,
Ceil,
}
use ::Round::{Floor, Ceil};
fn round(n: f64, setting: Round) -> f64 {
match setting {
Floor => n.floor(),
Ceil => n.ceil(),
}
}

第二种变体是使用闭包。

fn round_func(setting: Round) -> impl Fn(f64) -> f64 {
move |n| match setting {
Floor => n.floor(),
Ceil => n.ceil(),
}
}

这将以您的设置作为输入,并返回一个闭合,该闭合将为您计算地板或天花板。EG你可以做一些类似的事情

let closure = round_func(setting);
println!("{}", closure(5.5));
println!("{}", closure(5.5));

这将把配置作为输入,并返回一个用作函数的闭包。

在这种特殊情况下,我们实际上可以通过直接返回函数指针来做得更好。代码如下:

fn round_func_pointer(setting: Round) -> fn(f64) -> f64 {
match setting {
Floor => f64::floor,
Ceil => f64::ceil,
}
}

代码也可以写为

fn round_func_pointer(setting: Round) -> fn(f64) -> f64 {
match setting {
Floor => |n| n.floor(),
Ceil => |n| n.ceil(),
}
}

这是因为";闭包";|n| n.floor()实际上根本不捕获任何内容(不需要move关键字(,因此被编译为函数指针。

类变量w/精度和舍入模式

Rust没有像C++、Java和其他语言那样将结构中的字段声明为静态的语法。然而,仍然可以声明一个静态变量,该静态变量对于所有意图和目的来说都是"0";类变量";。

在下面的实现中,静态变量CONFIG的作用域被限制为它在其中声明的文件;因此,从这个意义上说,它不是一个";全局";变量在同一文件中声明的结构对其具有独占访问权限,并且该结构的所有实例都访问同一变量。

为了确保";类变量";RwLock用于同步它。与在单线程环境中不同步访问的实现相比,这会带来较小的性能成本。

CCD_ 13宏建立";类变量";以延迟初始化,直到运行时实际需要该变量。

use std::sync::RwLock;
#[macro_use]
extern crate lazy_static;
lazy_static! {
// The tuple holds (rounding-mode, precision).
static ref CONFIG: RwLock<(RoundMode, u32)> = {
RwLock::new((RoundMode::Auto, 3))
};
}
#[derive(Clone, Copy)]
pub enum RoundMode { Ceil, Floor, Auto }

下面的结构实现为客户端代码提供了一种设置HasValue所有实例的舍入模式(Auto、Ceil和Floor(和精度(要舍入到的小数位数(的方法。

pub struct HasValue { value: f64 }
impl HasValue {
pub fn new(value: f64) -> Self
{
HasValue { value }
}
pub fn config() -> (RoundMode, u32)
{
*CONFIG.read().unwrap()
}
pub fn set_config(mode: RoundMode, precision: u32)
{
*CONFIG.write().unwrap() = (mode, precision);
}
pub fn round(&self) -> f64
{
use RoundMode::*;

let (mode, prec) = HasValue::config();
let base = 10_f64.powf(prec as f64);

match mode {
Auto  => (self.value * base).round() / base,
Ceil  => (self.value * base).ceil()  / base,
Floor => (self.value * base).floor() / base,
}
}
}

正在创建、配置、打印。。。

fn main() 
{
let v = HasValue::new(7.98555);
let r = v.round();
println!("r={:?}", r);

HasValue::set_config(RoundMode::Floor, 4);
let r = v.round();
println!("r={:?}", r);
}

相关内容

最新更新