Expr

Enum Expr 

Source
pub enum Expr<T>
where T: Clone + Debug + Eq + Hash,
{ Terminal(T), Const(bool), Not(Box<Expr<T>>), And(Box<Expr<T>>, Box<Expr<T>>), Or(Box<Expr<T>>, Box<Expr<T>>), }
Expand description

An Expr is a simple Boolean logic expression. It may contain terminals (i.e., free variables), constants, and the following fundamental operations: AND, OR, NOT.

use std::collections::HashMap;
use boolean_expression::Expr;

#[derive(Clone, Hash, Eq, PartialEq, Debug)]
enum RiverCrossing { Chicken, Fox, Grain }

let chicken = Expr::Terminal(RiverCrossing::Chicken);
let fox_and_grain = Expr::Terminal(RiverCrossing::Fox) & Expr::Terminal(RiverCrossing::Grain);

let allowed = (!chicken.clone() & fox_and_grain.clone()) | (chicken & !fox_and_grain);
let items = [
   (RiverCrossing::Grain, true),
   (RiverCrossing::Fox, true),
].iter().cloned().collect();

// nobody gets eaten!
assert!(allowed.evaluate(&items));

Variants§

§

Terminal(T)

A terminal (free variable). This expression node represents a value that is not known until evaluation time.

§

Const(bool)

A boolean constant: true or false.

§

Not(Box<Expr<T>>)

The logical complement of the contained expression argument.

§

And(Box<Expr<T>>, Box<Expr<T>>)

The logical AND of the two expression arguments.

§

Or(Box<Expr<T>>, Box<Expr<T>>)

The logical OR of the two expression arguments.

Implementations§

Source§

impl<T> Expr<T>
where T: Clone + Debug + Eq + Hash,

Source

pub fn is_terminal(&self) -> bool

Returns true if this Expr is a terminal.

Source

pub fn is_const(&self) -> bool

Returns true if this Expr is a constant.

Source

pub fn is_not(&self) -> bool

Returns true if this Expr is a NOT node.

Source

pub fn is_and(&self) -> bool

Returns true if this Expr is an AND node.

Source

pub fn is_or(&self) -> bool

Returns true if this Expr is an OR node.

Source

pub fn not(e: Expr<T>) -> Expr<T>

Builds a NOT node around an argument, consuming the argument expression.

Source

pub fn and(e1: Expr<T>, e2: Expr<T>) -> Expr<T>

Builds an AND node around two arguments, consuming the argument expressions.

Source

pub fn or(e1: Expr<T>, e2: Expr<T>) -> Expr<T>

Builds an OR node around two arguments, consuming the argument expressions.

Source

pub fn xor(e1: Expr<T>, e2: Expr<T>) -> Expr<T>

Source

pub fn evaluate(&self, vals: &HashMap<T, bool>) -> bool

Evaluates the expression with a particular set of terminal assignments. If any terminals are not assigned, they default to false.

Source

pub fn evaluate_with<F>(&self, f: F) -> bool
where F: Fn(&T) -> bool,

Evaluates the expression using the provided function to map terminals (variables) to boolean values. This is a generalization of Expr::evaluate, where the variable lookup in a hashmap is replaced with an arbitrary computation.

 use boolean_expression::Expr;

 let expression = Expr::Terminal(10) | Expr::Terminal(3);

 // check if the expression satisfies a predicate
 assert!(expression.evaluate_with(|&x| x > 5));
Source

pub fn simplify_via_laws(self) -> Expr<T>

Simplify an expression in a relatively cheap way using well-known logic identities.

This function performs certain reductions using DeMorgan’s Law, distribution of ANDs over ORs, and certain identities involving constants, to simplify an expression. The result will always be in sum-of-products form: nodes will always appear in order (from expression tree root to leaves) OR -> AND -> NOT. In other words, AND nodes may only have NOT nodes (or terminals or constants) as children, and NOT nodes may only have terminals or constants as children.

This function explicitly does not perform any sort of minterm reduction. The terms may thus be redundant (i.e., And(a, b) may appear twice), and combinable terms may exist (i.e., And(a, b) and And(a, Not(b)) may appear in the OR’d list of terms, where these could be combined to simply a but are not). For example:

use boolean_expression::Expr;

// This simplifies using DeMorgan's Law:
let expr = Expr::not(Expr::or(Expr::Terminal(0), Expr::Terminal(1)));
let simplified = expr.simplify_via_laws();
assert_eq!(simplified,
    Expr::and(Expr::not(Expr::Terminal(0)),
              Expr::not(Expr::Terminal(1))));

// This doesn't simplify, though:
let expr = Expr::or(
            Expr::and(Expr::Terminal(0), Expr::Terminal(1)),
            Expr::and(Expr::Terminal(0), Expr::not(Expr::Terminal(1))));
let simplified = expr.clone().simplify_via_laws();
assert_eq!(simplified, expr);

For better (but more expensive) simplification, see simplify_via_bdd().

Source

pub fn simplify_via_bdd(self) -> Expr<T>

Simplify an expression via a roundtrip through a BDD. This procedure is more effective than Expr::simplify_via_laws(), but more expensive. This roundtrip will implicitly simplify an arbitrarily complicated function (by construction, as the BDD is built), and then find a quasi-minimal set of terms using cubelist-based reduction. For example:

use boolean_expression::Expr;

// `simplify_via_laws()` cannot combine these terms, but
// `simplify_via_bdd()` will:
let expr = Expr::or(
            Expr::and(Expr::Terminal(0), Expr::Terminal(1)),
            Expr::and(Expr::Terminal(0), Expr::not(Expr::Terminal(1))));
let simplified = expr.simplify_via_bdd();
assert_eq!(simplified, Expr::Terminal(0));
Source

pub fn map<F, R>(&self, f: F) -> Expr<R>
where F: Fn(&T) -> R, R: Clone + Debug + Eq + Hash,

Map terminal values using the specified mapping function.

Trait Implementations§

Source§

impl<T> BitAnd for Expr<T>
where T: Clone + Debug + Eq + Hash,

Source§

type Output = Expr<T>

The resulting type after applying the & operator.
Source§

fn bitand(self, rhs: Expr<T>) -> Self::Output

Performs the & operation. Read more
Source§

impl<T> BitAndAssign for Expr<T>
where T: Clone + Debug + Eq + Hash,

Source§

fn bitand_assign(&mut self, rhs: Expr<T>)

Performs the &= operation. Read more
Source§

impl<T> BitOr for Expr<T>
where T: Clone + Debug + Eq + Hash,

Source§

type Output = Expr<T>

The resulting type after applying the | operator.
Source§

fn bitor(self, rhs: Expr<T>) -> Self::Output

Performs the | operation. Read more
Source§

impl<T> BitOrAssign for Expr<T>
where T: Clone + Debug + Eq + Hash,

Source§

fn bitor_assign(&mut self, rhs: Expr<T>)

Performs the |= operation. Read more
Source§

impl<T> BitXor for Expr<T>
where T: Clone + Debug + Eq + Hash,

Source§

type Output = Expr<T>

The resulting type after applying the ^ operator.
Source§

fn bitxor(self, rhs: Expr<T>) -> Self::Output

Performs the ^ operation. Read more
Source§

impl<T> BitXorAssign for Expr<T>
where T: Clone + Debug + Eq + Hash,

Source§

fn bitxor_assign(&mut self, rhs: Expr<T>)

Performs the ^= operation. Read more
Source§

impl<T> Clone for Expr<T>
where T: Clone + Debug + Eq + Hash + Clone,

Source§

fn clone(&self) -> Expr<T>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T> Debug for Expr<T>
where T: Clone + Debug + Eq + Hash + Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T> Hash for Expr<T>
where T: Clone + Debug + Eq + Hash + Hash,

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T> Not for Expr<T>
where T: Clone + Debug + Eq + Hash,

Source§

type Output = Expr<T>

The resulting type after applying the ! operator.
Source§

fn not(self) -> Self::Output

Performs the unary ! operation. Read more
Source§

impl<T> PartialEq for Expr<T>
where T: Clone + Debug + Eq + Hash + PartialEq,

Source§

fn eq(&self, other: &Expr<T>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<T> Eq for Expr<T>
where T: Clone + Debug + Eq + Hash + Eq,

Source§

impl<T> StructuralPartialEq for Expr<T>
where T: Clone + Debug + Eq + Hash,

Auto Trait Implementations§

§

impl<T> Freeze for Expr<T>
where T: Freeze,

§

impl<T> RefUnwindSafe for Expr<T>
where T: RefUnwindSafe,

§

impl<T> Send for Expr<T>
where T: Send,

§

impl<T> Sync for Expr<T>
where T: Sync,

§

impl<T> Unpin for Expr<T>
where T: Unpin,

§

impl<T> UnwindSafe for Expr<T>
where T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.