loops - 如何迭代或映射元组?

标签 loops tuples rust

我最初的问题是将不同类型的元组转换为字符串。在 Python 中,这类似于:

>> a = ( 1.3, 1, 'c' )
>> b = map(  lambda x:  str(x), a )
['1.3', '1', 'c']

>> " ".join(b)
'1.3 1 c"

然而,Rust 不支持元组上的映射——仅支持类似向量的结构。显然,这是由于能够将不同的类型打包到一个元组中,并且缺少函数重载。另外,我找不到在运行时获取元组长度的方法。所以,我想,需要一个宏来进行转换。

作为开始,我尝试匹配元组的头部,例如:

// doesn't work
match some_tuple {
    (a, ..) => println!("{}", a),
          _ => ()
}

所以,我的问题:

  1. 是否可以使用库函数将元组转换为字符串,指定任意分隔符?
  2. 如何编写能够将函数映射到任意大小的元组的宏?

最佳答案

这是一个过于聪明的宏解决方案:

trait JoinTuple {
    fn join_tuple(&self, sep: &str) -> String;
}

macro_rules! tuple_impls {
    () => {};

    ( ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )* ) => {
        impl<$typ, $( $ntyp ),*> JoinTuple for ($typ, $( $ntyp ),*)
        where
            $typ: ::std::fmt::Display,
            $( $ntyp: ::std::fmt::Display ),*
        {
            fn join_tuple(&self, sep: &str) -> String {
                let parts: &[&::std::fmt::Display] = &[&self.$idx, $( &self.$nidx ),*];
                parts.iter().rev().map(|x| x.to_string()).collect::<Vec<_>>().join(sep)
            }
        }

        tuple_impls!($( ($nidx => $ntyp), )*);
    };
}

tuple_impls!(
    (9 => J),
    (8 => I),
    (7 => H),
    (6 => G),
    (5 => F),
    (4 => E),
    (3 => D),
    (2 => C),
    (1 => B),
    (0 => A),
);

fn main() {
    let a = (1.3, 1, 'c');

    let s = a.join_tuple(", ");
    println!("{}", s);
    assert_eq!("1.3, 1, c", s);
}

基本思想是我们可以获取一个元组并将其解压到&[&fmt::Display]中。一旦我们有了它,就可以直接将每个项目映射到一个字符串中,然后将它们全部与分隔符组合起来。这是它自己的样子:

fn main() {
    let tup = (1.3, 1, 'c');

    let slice: &[&::std::fmt::Display] = &[&tup.0, &tup.1, &tup.2];
    let parts: Vec<_> = slice.iter().map(|x| x.to_string()).collect();
    let joined = parts.join(", ");

    println!("{}", joined);
}

下一步是创建特征并针对特定情况实现它:

trait TupleJoin {
    fn tuple_join(&self, sep: &str) -> String;
}

impl<A, B, C> TupleJoin for (A, B, C)
where
    A: ::std::fmt::Display,
    B: ::std::fmt::Display,
    C: ::std::fmt::Display,
{
    fn tuple_join(&self, sep: &str) -> String {
        let slice: &[&::std::fmt::Display] = &[&self.0, &self.1, &self.2];
        let parts: Vec<_> = slice.iter().map(|x| x.to_string()).collect();
        parts.join(sep)
    }
}

fn main() {
    let tup = (1.3, 1, 'c');

    println!("{}", tup.tuple_join(", "));
}

这只为特定大小的元组实现了我们的特性,这在某些情况下可能没问题,但肯定还不是standard library使用一些宏来减少您需要做的复制和粘贴的苦差事,以获得更多的尺寸。我决定变得更懒惰并减少该解决方案的复制和粘贴!

我没有清楚明确地列出元组的每个大小和相应的索引/通用名称,而是使我的宏递归。这样,我只需要列出一次,所有较小的尺寸都只是递归调用的一部分。不幸的是,我不知道如何让它向前发展,所以我只是把所有东西都翻转过来然后倒退。这意味着我们必须使用反向迭代器,这会导致效率低下,但总体而言,这应该是一个很小的代价。

关于loops - 如何迭代或映射元组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29148544/

相关文章:

Java 任务在循环后停止

python - 将两个列表合并为字典时保留顺序

list - 在 Haskell 元组列表上压缩相同的值

rust - 为什么连接路径会完全替换 Rust 中的原始路径?

Java 循环整个代码 3 次

java - 在 Java 中迭代 StringBuilder 的标准方法?

rust - 使用 println 打印不同次数的字符

rust - 如何在 Rust 中制作安全的静态单例?

c++ - 如何从开关内部跳出循环?

scala - 过滤 Scala Multimap 并输出为元组列表