Решение на Логически изрази от Георги Йорданов

Обратно към всички решения

Към профила на Георги Йорданов

Резултати

  • 16 точки от тестове
  • 0 бонус точки
  • 16 точки общо
  • 16 успешни тест(а)
  • 4 неуспешни тест(а)

Код

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr {
Atom(char),
Not(Box<Expr>),
And(Vec<Expr>),
Or(Vec<Expr>),
}
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
UnexpectedExpr,
UnexpectedUnaryOp,
UnexpectedBinOp,
UnexpectedParen,
UnexpectedEnd,
}
fn is_valid_atom_symbol(c: char) -> bool {
c != ' ' && c != '&' && c != '|' && c != '!' && c != '(' && c != ')'
}
fn is_valid_op_symbol(c: char) -> bool {
c == '&' || c == '|' || c == '!'
}
#[derive (Debug)]
pub struct SimpleExprParser {
symbols: Vec<char>,
}
impl SimpleExprParser {
pub fn new() -> SimpleExprParser {
SimpleExprParser { symbols: vec![] }
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
match self.symbols.last() {
Some(sym) => if is_valid_atom_symbol(*sym) {
Err(ParseError::UnexpectedExpr)
} else {
self.symbols.push(c);
Ok(())
},
None => {
self.symbols.push(c);
Ok(())
}
}
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
match self.symbols.last() {
Some('!') | Some('&') | Some('|') | None => if op == '!' {
self.symbols.push(op);
Ok(())
} else {
Err(ParseError::UnexpectedBinOp)
}
Some(_) => if op == '!' {
Err(ParseError::UnexpectedUnaryOp)
} else {
self.symbols.push(op);
Ok(())
}
}
}
pub fn finish(self) -> Result<Expr, ParseError> {
let mut it = self.symbols.into_iter().peekable();
let mut result: Expr = Expr::Atom(' ');
while let Some(ch) = it.next() {
if result == Expr::Atom(' ') {
if is_valid_atom_symbol(ch) {
result = Expr::Atom(ch);
continue;
} else {
let mut nots = 1;
while let Some(&next_ch) = it.peek() {
if next_ch == '!' {
nots += 1;
it.next();
} else {
break;
}
}
match it.next() {
Some(next_ch) if is_valid_atom_symbol(next_ch) => {
let mut expr = Expr::Atom(next_ch);
for i in 0..nots {
expr = Expr::Not(Box::new(expr));
}
result = expr;
continue;
},
Some(_) => {
return Err(ParseError::UnexpectedBinOp);
},
None => {
return Err(ParseError::UnexpectedEnd);
}
}
}
}
if is_valid_atom_symbol(ch) {
match &mut result {
Expr::And(val) | Expr::Or(val) => val.push(Expr::Atom(ch)),
_ => return Err(ParseError::UnexpectedExpr)
}
} else if ch == '!' {
match &mut result {
Expr::And(val) | Expr::Or(val) => {
let mut nots = 1;
while let Some(&next_ch) = it.peek() {
if next_ch == '!' {
nots += 1;
it.next();
} else {
break;
}
}
match it.next() {
Some(next_ch) if is_valid_atom_symbol(next_ch) => {
let mut expr = Expr::Atom(next_ch);
for i in 0..nots {
expr = Expr::Not(Box::new(expr));
}
val.push(expr);
continue;
},
Some(_) => {
return Err(ParseError::UnexpectedBinOp);
},
None => {
return Err(ParseError::UnexpectedEnd);
}
}
}
_ => return Err(ParseError::UnexpectedExpr),
}
} else if ch == '|' {
result = Expr::Or(vec![result]);
} else if ch == '&' {
result = Expr::And(vec![result]);
}
}
match &result {
Expr::And(vec) | Expr::Or(vec) => if vec.len() == 1 {
Err(ParseError::UnexpectedEnd)
} else {
Ok(result)
},
_ => Ok(result)
}
}
}
pub struct ExprParser {
symbols: Vec<char>,
}
impl ExprParser {
pub fn new() -> ExprParser {
ExprParser { symbols: vec![] }
}
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if let Some(sym) = self.symbols.last() {
match sym {
'(' | '&' | '|' | '!' => {
self.symbols.push(c);
Ok(())
},
_ => Err(ParseError::UnexpectedExpr),
}
} else {
self.symbols.push(c);
Ok(())
}
}
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
if let Some(sym) = self.symbols.last() {
match sym {
'!' => if op == '!' {
self.symbols.push(op);
Ok(())
} else {
Err(ParseError::UnexpectedBinOp)
},
'(' | '&' | '|' => if op == '!' {
self.symbols.push(op);
Ok(())
} else {
Err(ParseError::UnexpectedBinOp)
},
')' | _ => if op == '!' {
Err(ParseError::UnexpectedUnaryOp)
} else {
self.symbols.push(op);
Ok(())
}
}
} else {
if op == '!' {
self.symbols.push(op);
Ok(())
} else {
Err(ParseError::UnexpectedBinOp)
}
}
}
pub fn open_paren(&mut self) -> Result<(), ParseError> {
if let Some(sym) = self.symbols.last() {
match sym {
'(' | '&' | '|' | '!' => {
self.symbols.push('(');
Ok(())
},
_ => Err(ParseError::UnexpectedParen),
}
} else {
self.symbols.push('(');
Ok(())
}
}
pub fn close_paren(&mut self) -> Result<(), ParseError> {
if let Some(sym) = self.symbols.last() {
match sym {
'!' | '&' | '|' | '(' => Err(ParseError::UnexpectedParen),
')' | _ => {
self.symbols.push(')');
Ok(())
}
}
} else {
Err(ParseError::UnexpectedParen)
}
}
pub fn finish(self) -> Result<Expr, ParseError> {
self.balanced()?;
self.trailing_oper()?;
let mut expr_stack: Vec<Expr> = vec![];
let postfix = self.to_postfix();
for ch in postfix {
if is_valid_atom_symbol(ch) {
expr_stack.push(Expr::Atom(ch));
}
else if ch == '!' {
if let Some(expr) = expr_stack.pop() {
expr_stack.push(Expr::Not(Box::new(expr)))
} else {
return Err(ParseError::UnexpectedUnaryOp);
}
}
else if ch == '&' {
if expr_stack.len() < 2 {
println!("{:?}", expr_stack);
return Err(ParseError::UnexpectedBinOp);
}
let right = expr_stack.pop().unwrap();
let left = expr_stack.pop().unwrap();
match left {
Expr::And(mut vec) => {
vec.push(right);
expr_stack.push(Expr::And(vec));
},
_ => expr_stack.push(Expr::And(vec![left, right])),
}
}
else if ch == '|' {
if expr_stack.len() < 2 {
return Err(ParseError::UnexpectedBinOp);
}
let right = expr_stack.pop().unwrap();
let left = expr_stack.pop().unwrap();
match left {
Expr::Or(mut vec) => {
vec.push(right);
expr_stack.push(Expr::Or(vec));
},
_ => expr_stack.push(Expr::Or(vec![left, right])),
}
}
}
if expr_stack.len() != 1 {
Err(ParseError::UnexpectedExpr)
} else {
Ok(expr_stack.last().unwrap().clone())
}
}
// check for trailing operator in expressions such as: A &
fn trailing_oper(&self) -> Result<(), ParseError> {
match self.symbols.last() {
Some('!') | Some('&') | Some('|') | None => Err(ParseError::UnexpectedEnd),
Some(_) => Ok(())
}
}
// check for balancing parens
fn balanced(&self) -> Result<(), ParseError> {
let mut paren_stack: Vec<char> = vec![];
for c in &self.symbols {
if *c == '(' {
paren_stack.push(*c);
} else if *c == ')' {
if paren_stack.is_empty() {
return Err(ParseError::UnexpectedParen);
} else {
paren_stack.pop();
}
} else {
continue;
}
}
if paren_stack.is_empty() {
Ok(())
} else {
Err(ParseError::UnexpectedParen)
}
}
// shunting yard algorithm
fn to_postfix(&self) -> Vec<char> {
let mut postfix: Vec<char> = vec![];
let mut operator_stack: Vec<char> = vec![];
for c in &self.symbols {
if is_valid_atom_symbol(*c) {
postfix.push(*c);
}
else if is_valid_op_symbol(*c) {
while let Some(val) = operator_stack.last() {
if Self::get_priority(*val) >= Self::get_priority(*c) {
postfix.push(operator_stack.pop().unwrap());
} else {
break;
}
}
operator_stack.push(*c);
} else if *c == '(' {
operator_stack.push('(');
} else if *c == ')' {
while let Some(val) = operator_stack.last() {
if *val != '(' {
postfix.push(operator_stack.pop().unwrap())
} else {
operator_stack.pop();
break;
}
}
}
}
while let Some(c) = operator_stack.last() {
postfix.push(operator_stack.pop().unwrap());
}
postfix
}
fn get_priority(symbol: char) -> i32 {
if symbol == '!' {
2
} else if symbol == '&' || symbol == '|' {
1
} else {
0
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
True,
False,
Expr(Expr),
}
pub fn eval(expr: &Expr, truthy: &[char], falsy: &[char]) -> Value {
match expr {
Expr::Atom(_) => eval_atom(expr, truthy, falsy),
Expr::Not(_) => eval_not(expr, truthy, falsy),
Expr::And(_) => eval_and(expr, truthy, falsy),
Expr::Or(_) => eval_or(expr, truthy, falsy),
}
}
fn eval_atom(expr: &Expr, truthy: &[char], falsy: &[char]) -> Value {
if let Expr::Atom(ch) = expr {
if truthy.contains(ch) {
Value::True
} else if falsy.contains(ch) {
Value::False
} else {
Value::Expr(expr.clone())
}
} else {
// won't happen
Value::Expr(expr.clone())
}
}
fn eval_not(expr: &Expr, truthy: &[char], falsy: &[char]) -> Value {
if let Expr::Not(nested_expr) = expr {
match eval(&nested_expr, truthy, falsy) {
Value::True => Value::False,
Value::False => Value::True,
Value::Expr(evaluated) => Value::Expr(Expr::Not(Box::new(evaluated))),
}
} else {
// won't happen...
Value::Expr(expr.clone())
}
}
fn eval_and(expr: &Expr, truthy: &[char], falsy: &[char]) -> Value {
if let Expr::And(vec) = expr {
let evaluated = vec
.iter()
.map(|nested_expr| eval(nested_expr, truthy, falsy))
.collect::<Vec<_>>();
if evaluated.iter().all(|ev_expr| *ev_expr == Value::True) {
Value::True
} else if evaluated.iter().any(|ev_expr| *ev_expr == Value::False) {
Value::False
} else if evaluated.iter().all(|ev_expr| if let Value::Expr(_) | Value::True = ev_expr {true } else {false}) {
let filtered = evaluated.into_iter()
.filter_map(|ev_expr| if let Value::Expr(expr) = ev_expr {Some(expr.clone())} else {None} )
.collect::<Vec<Expr>>();
if filtered.len() < 2 {
Value::Expr(filtered.last().unwrap().clone())
} else {
Value::Expr(Expr::And(filtered
.into_iter()
.collect::<Vec<Expr>>()))
}
} else {
// Unreachable
Value::False
}
} else {
// won't happen...
Value::Expr(expr.clone())
}
}
fn eval_or(expr: &Expr, truthy: &[char], falsy: &[char]) -> Value {
if let Expr::Or(vec) = expr {
let evaluated = vec
.iter()
.map(|nested_expr| eval(nested_expr, truthy, falsy))
.collect::<Vec<_>>();
if evaluated.iter().all(|ev_expr| *ev_expr == Value::False) {
Value::False
} else if evaluated.iter().any(|ev_expr| *ev_expr == Value::True) {
Value::True
} else if evaluated.iter().all(|ev_expr| if let Value::Expr(_) | Value::False = ev_expr {true } else {false}) {
let filtered = evaluated.into_iter()
.filter_map(|ev_expr| if let Value::Expr(expr) = ev_expr {Some(expr.clone())} else {None} )
.collect::<Vec<Expr>>();
if filtered.len() < 2 {
Value::Expr(filtered.last().unwrap().clone())
} else {
Value::Expr(Expr::And(filtered
.into_iter()
.collect::<Vec<Expr>>()))
}
} else {
// Unreachable
Value::False
}
} else {
// won't happen...
Value::Expr(expr.clone())
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20241224-258381-jrin8l/solution)
warning: unused variable: `i`
  --> src/lib.rs:98:33
   |
98 | ...                   for i in 0..nots {
   |                           ^ help: if this is intentional, prefix it with an underscore: `_i`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `i`
   --> src/lib.rs:135:37
    |
135 | ...                   for i in 0..nots {
    |                           ^ help: if this is intentional, prefix it with an underscore: `_i`

warning: unused variable: `c`
   --> src/lib.rs:401:24
    |
401 |         while let Some(c) = operator_stack.last() {
    |                        ^ help: if this is intentional, prefix it with an underscore: `_c`

warning: `solution` (lib) generated 3 warnings
warning: variable does not need to be mutable
   --> tests/solution_test.rs:306:13
    |
306 |         let mut parser = SimpleExprParser::new();
    |             ----^^^^^^
    |             |
    |             help: remove this `mut`
    |
    = note: `#[warn(unused_mut)]` on by default

warning: `solution` (test "solution_test") generated 1 warning
    Finished test [unoptimized + debuginfo] target(s) in 1.89s
     Running tests/solution_test.rs (target/debug/deps/solution_test-1428e1090729d165)

running 20 tests
test solution_test::test_error_paren_mismatched ... ok
test solution_test::test_eval_full ... ok
test solution_test::test_eval_not_and_or ... ok
test solution_test::test_eval_partial ... FAILED
test solution_test::test_eval_unwrap_and_or ... ok
test solution_test::test_eval_unwrap_nested ... FAILED
test solution_test::test_paren_around_expr ... ok
test solution_test::test_paren_expr_priority ... ok
test solution_test::test_paren_not ... ok
test solution_test::test_paren_nested ... ok
test solution_test::test_paren_surrounded ... ok
test solution_test::test_parser_alternating_ops ... ok
test solution_test::test_parser_and_or ... ok
test solution_test::test_parser_atom ... ok
test solution_test::test_parser_error_unexpected_end ... FAILED
test solution_test::test_parser_errors_basic ... ok
test solution_test::test_parser_expr_and_not ... ok
test solution_test::test_parser_multiple_not ... ok
test solution_test::test_parser_multiple_atoms_same_op ... FAILED
test solution_test::test_parser_not ... ok

failures:

---- solution_test::test_eval_partial stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `Expr(And([Atom('A'), Atom('C')]))`,
 right: `Expr(Or([Atom('A'), Atom('C')]))`', tests/solution_test.rs:429:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'solution_test::test_eval_partial' panicked at 'assertion failed: `(left == right)`
  left: `Expr(And([Atom('A'), Atom('C')]))`,
 right: `Expr(Or([Atom('A'), Atom('C')]))`', tests/solution_test.rs:409:5

---- solution_test::test_eval_unwrap_nested stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `Expr(And([Atom('X'), Atom('B'), Not(Atom('D')), Atom('Y')]))`,
 right: `Expr(Or([Atom('X'), Atom('B'), Not(Atom('D')), Atom('Y')]))`', tests/solution_test.rs:480:9
thread 'solution_test::test_eval_unwrap_nested' panicked at 'assertion failed: `(left == right)`
  left: `Expr(And([Atom('X'), Atom('B'), Not(Atom('D')), Atom('Y')]))`,
 right: `Expr(Or([Atom('X'), Atom('B'), Not(Atom('D')), Atom('Y')]))`', tests/solution_test.rs:479:5

---- solution_test::test_parser_error_unexpected_end stdout ----
thread '<unnamed>' panicked at 'assertion failed: matches!(parser.finish(), Err(_))', tests/solution_test.rs:307:9
thread 'solution_test::test_parser_error_unexpected_end' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:305:5

---- solution_test::test_parser_multiple_atoms_same_op stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `And([And([Atom('A'), Atom('B')]), Atom('C')])`,
 right: `And([Atom('A'), Atom('B'), Atom('C')])`', tests/solution_test.rs:127:9
thread 'solution_test::test_parser_multiple_atoms_same_op' panicked at 'assertion failed: `(left == right)`
  left: `And([And([Atom('A'), Atom('B')]), Atom('C')])`,
 right: `And([Atom('A'), Atom('B'), Atom('C')])`', tests/solution_test.rs:124:5


failures:
    solution_test::test_eval_partial
    solution_test::test_eval_unwrap_nested
    solution_test::test_parser_error_unexpected_end
    solution_test::test_parser_multiple_atoms_same_op

test result: FAILED. 16 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--test solution_test`

История (1 версия и 0 коментара)

Георги качи първо решение на 22.12.2024 15:07 (преди 9 месеца)