pub enum Expr<T>{
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>
impl<T> Expr<T>
Sourcepub fn is_terminal(&self) -> bool
pub fn is_terminal(&self) -> bool
Returns true if this Expr is a terminal.
Sourcepub fn not(e: Expr<T>) -> Expr<T>
pub fn not(e: Expr<T>) -> Expr<T>
Builds a NOT node around an argument, consuming the argument expression.
Sourcepub fn and(e1: Expr<T>, e2: Expr<T>) -> Expr<T>
pub fn and(e1: Expr<T>, e2: Expr<T>) -> Expr<T>
Builds an AND node around two arguments, consuming the argument expressions.
Sourcepub fn or(e1: Expr<T>, e2: Expr<T>) -> Expr<T>
pub fn or(e1: Expr<T>, e2: Expr<T>) -> Expr<T>
Builds an OR node around two arguments, consuming the argument expressions.
pub fn xor(e1: Expr<T>, e2: Expr<T>) -> Expr<T>
Sourcepub fn evaluate(&self, vals: &HashMap<T, bool>) -> bool
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.
Sourcepub fn evaluate_with<F>(&self, f: F) -> bool
pub fn evaluate_with<F>(&self, f: F) -> 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));Sourcepub fn simplify_via_laws(self) -> Expr<T>
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().
Sourcepub fn simplify_via_bdd(self) -> Expr<T>
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));Trait Implementations§
Source§impl<T> BitAndAssign for Expr<T>
impl<T> BitAndAssign for Expr<T>
Source§fn bitand_assign(&mut self, rhs: Expr<T>)
fn bitand_assign(&mut self, rhs: Expr<T>)
&= operation. Read moreSource§impl<T> BitOrAssign for Expr<T>
impl<T> BitOrAssign for Expr<T>
Source§fn bitor_assign(&mut self, rhs: Expr<T>)
fn bitor_assign(&mut self, rhs: Expr<T>)
|= operation. Read moreSource§impl<T> BitXorAssign for Expr<T>
impl<T> BitXorAssign for Expr<T>
Source§fn bitxor_assign(&mut self, rhs: Expr<T>)
fn bitxor_assign(&mut self, rhs: Expr<T>)
^= operation. Read moreimpl<T> Eq for Expr<T>
impl<T> StructuralPartialEq for Expr<T>
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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