creature_feature 0.2.0

Composable n-gram combinators that are ergonomic and bare-metal fast.
Documentation
use crate::accum_ftzr::{Ftzr, IterFtzr, LinearFixed};

use crate::as_tokens::AsTokens;

#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};

/// A fixed-length n-gram over referenced data. Created with `n_slice(n)`
#[derive(Hash, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct SliceGram {
    n: usize,
}

/// general n-grams over referenced data, produces borrowed data (like &str) or multiple `&[T]` of a fixed length. (Compare to `n_gram`)
/// ```
/// use creature_feature::ftzrs::n_slice;
/// use creature_feature::traits::Ftzr;
///
/// let my_ftzr = n_slice(7);
///
/// let ints = [1, 2, 3, 4, 5, 6, 7, 8, 9];
/// let feats: Vec<&[i32]> = my_ftzr.featurize(&ints[..]);
/// let feats: Vec<&str>   = my_ftzr.featurize("0123456789");
/// ```
pub fn n_slice(n: usize) -> SliceGram {
    SliceGram { n }
}

/// bigrams over referenced data, produces borrowed data (like &str) or multiple `&[T]` of length 2. (Compare to `bigram`)
pub fn bislice() -> SliceGram {
    SliceGram { n: 2 }
}

/// trigrams over referenced data, produces borrowed data (like &str) or multiple `&[T]` of length 3. (Compare to `trigram`)
pub fn trislice() -> SliceGram {
    SliceGram { n: 3 }
}

/// The associated iterator for SliceGram as IterFtzr<T>>::Iter
#[derive(Hash, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug)]
pub struct SliceGramIter<Origin> {
    n: usize,
    idx: usize,
    data: Origin,
}

impl<'a, T: 'a> Iterator for SliceGramIter<&'a [T]> {
    type Item = &'a [T];
    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        let jdx = self.idx + self.n;
        if jdx <= self.data.len() {
            let ret = Some(&self.data[self.idx..jdx]);
            self.idx += 1;
            ret
        } else {
            None
        }
    }
}

impl LinearFixed for SliceGram {
    fn chunk_size(&self) -> usize {
        self.n
    }
}
// One blanket over `D: AsTokens` replaces the former base `&[T]` impl plus the
// four `impl_ftrzs!`-generated forwards (`&Vec<T>`, `&[T; N]`, `&str`, `&String`).
impl<'a, D, T: 'a> IterFtzr<&'a D> for SliceGram
where
    D: AsTokens<Token = T> + ?Sized,
{
    type TokenGroup = &'a [T];
    type Iter = SliceGramIter<&'a [T]>;

    fn iterate_features(&self, origin: &'a D) -> Self::Iter {
        SliceGramIter {
            n: self.n,
            idx: 0,
            data: origin.as_tokens(),
        }
    }
}

// The `Ftzr` view forwards to `IterFtzr` per-featurizer (kept per-type, not a
// crate-wide blanket, so visitor-only featurizers keep their own `Ftzr` impls).
impl<Origin> Ftzr<Origin> for SliceGram
where
    Self: IterFtzr<Origin>,
{
    type TokenGroup = <Self as IterFtzr<Origin>>::TokenGroup;
    fn push_tokens<Push>(&self, origin: Origin, push: &mut Push)
    where
        Push: FnMut(Self::TokenGroup),
    {
        for t in self.iterate_features(origin) {
            push(t)
        }
    }
}