creature_feature 0.2.0

Composable n-gram combinators that are ergonomic and bare-metal fast.
Documentation
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
use std::collections::{LinkedList, VecDeque};
use std::iter::FromIterator;
use std::ops::Deref;
use std::rc::Rc;
use std::str::from_utf8;
use std::sync::Arc;

///A `Bag` is a wrapper for `HashMap` and `BTreeMap` so that they can be treated as a multiset. `Accumulates<Token>` can create either a mapping that only keeps the most recently seen value, or the count of the key (a bag).
///
/// `Bag<SomeMap<K, V>>` must have a counter type `V` implementing `From<u8> + AddAssign` (e.g. `u8`, `i16`, `u32`, ...), whereas `SomeMap<K, V>` doesn't have this limitation.
/// ```
/// use creature_feature::convert::Bag;
/// use creature_feature::ftzrs::{for_each, whole};
/// use creature_feature::traits::Ftzr;
/// use std::collections::HashMap;
///
/// let sentence = "one fish two fish red fish blue fish".split_ascii_whitespace();
/// let bag_of_words: Bag<HashMap<&str, i16>> = for_each(whole()).featurize(sentence);
/// assert_eq!(bag_of_words.get("fish"), Some(&4));
/// ```
#[derive(Hash, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Default)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Bag<T>(pub T);

/// `Merged` is a wrapper to mark a feature as being produced by one or more different types.
///
/// For example: `FeatureFrom<Result<A,B>>` or any featurizer produced by composing two featurizers with different outputs (like `bookends` or `featurizers!`)
/// ```
/// use creature_feature::featurizers;
/// use creature_feature::convert::Merged;
/// use creature_feature::ftzrs::{bislice, bookends, trigram};
/// use creature_feature::traits::Ftzr;
/// use creature_feature::HashedAs;
///
/// // neither of these will compile without Merged
/// let feats: Vec<Merged<String>> = featurizers!(bislice(), trigram()).featurize("sesquipedalian");
/// let feats: Vec<Merged<HashedAs<u16>>> = bookends((bislice(), 3), (trigram(), 4)).featurize("sesquipedalian");
/// ```
#[derive(Hash, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Default)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Merged<T>(pub T);

#[derive(Hash, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Debug, Default)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub(crate) struct Output<T>(pub T);

macro_rules! impl_deref {
    ($t:ty) => {
        impl<T> Deref for $t {
            type Target = T;
            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }
    };
}

impl_deref!(Output<T>);
impl_deref!(Bag<T>);
impl_deref!(Merged<T>);

const UNICODE_ERR_MSG: &str = "Featurizing as a &str is only supported for ascii. Please use &[u8] or Vec<char>. (This is at the bottom of the first page of the docs.)";

impl<'a> From<Output<&'a [u8]>> for &'a str {
    fn from(token_group: Output<&'a [u8]>) -> Self {
        from_utf8(&token_group).expect(UNICODE_ERR_MSG)
    }
}

impl<'a, T> From<Output<&'a [T]>> for &'a [T] {
    fn from(token_group: Output<&'a [T]>) -> Self {
        &token_group
    }
}

impl<T, const N: usize> From<Output<[T; N]>> for [T; N] {
    fn from(token_group: Output<[T; N]>) -> Self {
        token_group.0
    }
}

impl<const N: usize> From<Output<[u8; N]>> for String {
    fn from(token_group: Output<[u8; N]>) -> Self {
        from_utf8(&*token_group).expect(UNICODE_ERR_MSG).to_owned()
    }
}
impl<const N: usize> From<Output<[char; N]>> for String {
    fn from(token_group: Output<[char; N]>) -> Self {
        Iterator::collect(token_group.0.iter())
    }
}

impl<'a> From<Output<&'a [char]>> for String {
    fn from(token_group: Output<&'a [char]>) -> Self {
        let mut s = String::default();
        for c in token_group.0 {
            s.push(*c);
        }
        s
    }
}

macro_rules! group_from_iter {
    ($iter:ty) => {
        impl<Feature1: Copy, Feature2: From<Feature1>, const N: usize> From<Output<[Feature1; N]>>
            for $iter
        {
            fn from(token_group: Output<[Feature1; N]>) -> Self {
                Self::from_iter(token_group.0.iter().map(|x| From::from(*x)))
            }
        }
    };
}

group_from_iter!(Box<[Feature2]>);
group_from_iter!(Rc<[Feature2]>);
group_from_iter!(Arc<[Feature2]>);
group_from_iter!(VecDeque<Feature2>);
//group_from_iter!(BTreeMap);
group_from_iter!(Vec<Feature2>);
group_from_iter!(LinkedList<Feature2>);
//TODO make hashset work for any hasher
//group_from_iter!(HashSet<Feature>);

//requires cmp
//group_from_iter!(BinaryHeap<Feature>);
//group_from_iter!(BTreeSet<Feature>);