【问题标题】:How to achieve equivalent of take_while on a slice?如何在切片上实现等效的 take_while?
【发布时间】:2015-02-12 05:52:18
【问题描述】:

Rust 切片目前不支持某些迭代器方法,即 take_while。为切片实现 take_while 的最佳方法是什么?

const STRHELLO:&'static[u8] = b"HHHello";

fn main() {
    let subslice:&[u8] = STRHELLO.iter().take_while(|c|(**c=='H' as u8)).collect();
    println!("Expecting: {}, Got {}",STRHELLO.slice_to(3),subslice);
    assert!(subslice==STRHELLO.slice_to(3));
}

导致错误:

<anon>:6:74: 6:83 error: the trait `core::iter::FromIterator<&u8>` is not implemented for the type `&[u8]`

围栏中的这段代码: http://is.gd/1xkcUa

【问题讨论】:

    标签: rust slice


    【解决方案1】:

    首先,您遇到的问题是 collect 是关于创建一个 new 集合,而切片是关于引用现有数组中的连续范围的项目(无论是动态分配的)与否)。

    恐怕由于特征的性质,原始容器(STRHELLO)是一个连续范围的事实已经丢失,并且无法在事后重建。我还担心任何“通用”迭代器的使用都无法产生所需的输出;类型系统必须以某种方式承载以下事实:

    • 原始容器是一个连续的范围
    • 到目前为止执行的操作链保留了此属性

    这可能可行或不可行,但我现在还没有看到它完成,我不确定以何种方式可以优雅地实现它。


    另一方面,您可以自己动手做:

    fn take_while<'a>(initial: &'a [u8], predicate: |&u8| -> bool) -> &'a [u8] { // '
        let mut i = 0u;
        for c in initial.iter() {
            if predicate(c) { i += 1; } else { break; }
        }
        initial.slice_to(i)
    }
    

    然后:

    fn main() {
        let subslice: &[u8] = take_while(STRHELLO, |c|(*c==b'H'));
        println!("Expecting: {}, Got {}",STRHELLO.slice_to(3), subslice);
        assert!(subslice == STRHELLO.slice_to(3));
    }
    

    注意:'H' as u8 可以改写为b'H',如图所示,与字符串对称。

    【讨论】:

    • 伙计,很好的答案!在我坐在这里并怀疑答案可能是这样的时候,你实际上实现了它。也许切片上的 iter() 可以改为一次返回包含单个项目的子切片,然后您可以使用 collect 重新合并冗余子切片的东西?
    • @AndrewWagner:说实话,我只是不知道这里的 API 是什么。
    • 仍然只是吐口水,但如果我的总体想法可行,也许你只需要each()?生成迭代器,也许 splice() 将事物拼接在一起?也许 splice 会为不连续的结果返回切片的迭代器...
    • @AndrewWagner:在没有编译时检查的情况下,返回“尽可能连接”的切片迭代器的想法似乎很简洁。
    【解决方案2】:

    可以通过一些繁重的体操来使用库存迭代器来实现此功能:

    use std::raw::Slice;
    use std::mem::transmute;
    
    /// Splice together to slices of the same type that are contiguous in memory.
    /// Panics if the slices aren't contiguous with "a" coming first.
    /// i.e. slice b must follow slice a immediately in memory.
    fn splice<'a>(a:&'a[u8], b:&'a[u8]) -> &'a[u8] {
        unsafe {
            let aa:Slice<u8> = transmute(a);
            let bb:Slice<u8> = transmute(b);
            let pa = aa.data as *const u8;
            let pb = bb.data as *const u8;
            let off = aa.len as int; // Risks overflow into negative!!!
            assert!(pa.offset(off) == pb, "Slices were not contiguous!");
            let cc = Slice{data:aa.data,len:aa.len+bb.len};
            transmute(cc)
        }
    }
    
    /// Wrapper around splice that lets you use None as a base case for fold
    /// Will panic if the slices cannot be spliced!  See splice.
    fn splice_for_fold<'a>(oa:Option<&'a[u8]>, b:&'a[u8]) -> Option<&'a[u8]> {
       match oa {
           Some(a) => Some(splice(a,b)),
           None => Some(b),
       }
    }
    
    /// Implementaton using pure iterators
    fn take_while<'a>(initial: &'a [u8], 
                       predicate: |&u8| -> bool) -> Option<&'a [u8]> {
        initial
            .chunks(1)
            .take_while(|x|(predicate(&x[0])))
            .fold(None, splice_for_fold)
    }
    

    用法:

    const STRHELLO:&'static[u8] = b"HHHello";
    let subslice: &[u8] = super::take_while(STRHELLO, |c|(*c==b'H')).unwrap();
    println!("Expecting: {}, Got {}",STRHELLO.slice_to(3), subslice);
    assert!(subslice == STRHELLO.slice_to(3));
    

    如果您只需要 take_while,Matthieu 的实现会更加简洁。无论如何我都会发布这个,因为它可能是解决在切片上干净地使用迭代器函数的更普遍问题的途径。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-31
      • 2015-04-30
      • 1970-01-01
      • 1970-01-01
      • 2013-04-12
      • 2011-02-25
      相关资源
      最近更新 更多