Решение на Логически изрази от Милица Тончева

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

Към профила на Милица Тончева

Резултати

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

Код

use std::cmp::max;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr {
Atom(char),
Not(Box<Expr>),
And(Vec<Expr>),
Or(Vec<Expr>),
}
pub fn atom(c: char) -> Expr {
Expr::Atom(c)
}
pub fn not(e: Expr) -> Expr {
Expr::Not(Box::new(e))
}
pub fn and(e: Vec<Expr>) -> Expr {
Expr::And(e)
}
pub fn or(e: Vec<Expr>) -> Expr {
Expr::Or(e)
}
pub fn is_valid_atom(c: char) -> bool {
!is_valid_binary_op(c) &&
!is_valid_unary_op(c) &&
!is_open_paren(c) &&
!is_close_paren(c) &&
!c.is_whitespace()
}
pub fn is_valid_binary_op(c: char) -> bool {
c == '&' || c == '|'
}
pub fn is_valid_unary_op(c: char) -> bool {
c == '!'
}
pub fn is_open_paren(c: char) -> bool {
c == '('
}
pub fn is_close_paren(c: char) -> bool {
c == ')'
}
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError {
UnexpectedExpr,
UnexpectedUnaryOp,
UnexpectedBinOp,
UnexpectedParen,
UnexpectedEnd,
}
/// Парсър за прост израз, който не съдържа скоби
pub struct SimpleExprParser {
atoms: Vec<char>,
operators: Vec<char>,
last_el: char,
needed_atoms: usize,
}
impl SimpleExprParser {
pub fn new() -> SimpleExprParser {
SimpleExprParser {
atoms: Vec::new(),
operators: Vec::new(),
last_el: ' ',
needed_atoms: 1,
}
}
/// Приема атом.
///
/// c ще бъде валиден символ за атом.
/// В противен случай можете да panic-нете (няма да се тества)
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if !is_valid_atom(c) {
panic!("invalid character for atom");
}
if !is_valid_binary_op(self.last_el) &&
!is_valid_unary_op(self.last_el) &&
!self.last_el.is_whitespace() {
return Err(ParseError::UnexpectedExpr);
}
self.atoms.push(c);
self.last_el = c;
Result::Ok(())
}
fn push_binary(&mut self, op: char) -> Result<(), ParseError> {
if !is_valid_atom(self.last_el) {
return Err(ParseError::UnexpectedBinOp);
}
self.last_el = op;
self.operators.push(op);
if self.needed_atoms == 0 {
self.needed_atoms = 2;
}
else {
self.needed_atoms += 1;
}
Result::Ok(())
}
fn push_unary(&mut self, op: char) -> Result<(), ParseError> {
if is_valid_atom(self.last_el) {
return Err(ParseError::UnexpectedUnaryOp);
}
self.last_el = op;
self.operators.push(op);
self.needed_atoms = max(self.needed_atoms, 1);
Result::Ok(())
}
/// Приема символ за операция.
///
/// op ще бъде едно от '&', '|', '!'.
/// В противен случай можете да panic-нете (няма да се тества)
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
if !is_valid_binary_op(op) && !is_valid_unary_op(op) {
panic!("invalid character for operator");
}
if is_valid_binary_op(op) {
self.push_binary(op)
}
else {
self.push_unary(op)
}
}
/// Завършва парсването и връща построения израз.
pub fn finish(self) -> Result<Expr, ParseError> {
if self.atoms.is_empty() ||
self.atoms.len() != self.needed_atoms {
return Result::Err(ParseError::UnexpectedExpr);
}
let mut curr_expr: Expr = Expr::Atom(self.atoms[0]);
if self.operators.is_empty() {
return Ok(curr_expr);
}
let mut i:usize = 1;
let mut j:usize = 0;
if self.operators[0] == '!' {
curr_expr = not(curr_expr);
j += 1;
if self.operators.len() == 1 {
return Ok(curr_expr);
}
}
while j < self.operators.len(){
if self.operators[j].eq(&'&') {
if j+1 < self.operators.len() && is_valid_unary_op(self.operators[j + 1]){
curr_expr = and(vec![curr_expr, not(atom(self.atoms[i]))]);
j+=1;
}
else {
curr_expr = and(vec![curr_expr, atom(self.atoms[i])]);
}
i+=1;
j+=1;
}
else if self.operators[j].eq(&'|') {
if j+1 < self.operators.len() && is_valid_unary_op(self.operators[j + 1]){
curr_expr = or(vec![curr_expr, not(atom(self.atoms[i]))]);
j+=1;
}
else {
curr_expr = or(vec![curr_expr, atom(self.atoms[i])]);
}
i+=1;
j+=1;
}
else {
curr_expr = not(atom(self.atoms[i]));
i+=1;
j+=1;
}
}
Result::Ok(curr_expr)
}
}
/// Парсър за пълния израз
pub struct ExprParser {
complex_atoms: Vec<Vec<char>>,
operators: Vec<char>,
last_el: char,
needed_atoms: usize,
opened_paren: usize,
complex_atoms_index: usize
}
impl ExprParser {
pub fn new() -> ExprParser {
ExprParser {
complex_atoms: Vec::new(),
operators: Vec::new(),
last_el: ' ',
needed_atoms: 1,
opened_paren: 0,
complex_atoms_index: 0
}
}
/// Приема атом.
///
/// c ще бъде валиден символ за атом.
/// В противен случай можете да panic-нете (няма да се тества)
pub fn push_atom(&mut self, c: char) -> Result<(), ParseError> {
if !is_valid_atom(c) {
panic!("invalid character for atom");
}
if !is_valid_binary_op(self.last_el) &&
!is_valid_unary_op(self.last_el) &&
!self.last_el.is_whitespace() &&
self.last_el != '('{
return Err(ParseError::UnexpectedExpr);
}
if self.opened_paren == 0 {
self.complex_atoms.push(Vec::new());
self.complex_atoms_index = self.complex_atoms.len() - 1;
}
self.complex_atoms[self.complex_atoms_index].push(c);
self.last_el = c;
Result::Ok(())
}
fn push_binary(&mut self, op: char) -> Result<(), ParseError> {
if !is_valid_atom(self.last_el) &&
!is_close_paren(self.last_el){
return Err(ParseError::UnexpectedBinOp);
}
if self.opened_paren > 0 {
self.complex_atoms[self.complex_atoms_index].push(op);
}
else {
self.operators.push(op);
}
if self.opened_paren == 0 {
if self.needed_atoms == 0 {
self.needed_atoms = 2;
} else {
self.needed_atoms += 1;
}
}
self.last_el = op;
Result::Ok(())
}
fn push_unary(&mut self, op: char) -> Result<(), ParseError> {
if is_valid_atom(self.last_el) || is_close_paren(self.last_el) {
return Err(ParseError::UnexpectedUnaryOp);
}
if self.opened_paren > 0 {
self.complex_atoms[self.complex_atoms_index].push(op);
}
else {
self.operators.push(op);
}
self.last_el = op;
if self.opened_paren == 0 {
self.needed_atoms = max(self.needed_atoms, 1);
}
Result::Ok(())
}
/// Приема символ за операция.
///
/// op ще бъде едно от '&', '|', '!'.
/// В противен случай можете да panic-нете (няма да се тества)
pub fn push_op(&mut self, op: char) -> Result<(), ParseError> {
if !is_valid_binary_op(op) && !is_valid_unary_op(op) {
panic!("invalid character for operator");
}
if is_valid_binary_op(op) {
self.push_binary(op)
}
else {
self.push_unary(op)
}
}
/// Приема отваряща скоба.
pub fn open_paren(&mut self) -> Result<(), ParseError> {
if is_valid_atom(self.last_el) {
return Result::Err(ParseError::UnexpectedParen);
}
self.last_el = '(';
self.complex_atoms.push(Vec::new());
self.complex_atoms_index = self.complex_atoms.len() - 1;
self.opened_paren += 1;
Result::Ok(())
}
/// Приема затваряща скоба.
pub fn close_paren(&mut self) -> Result<(), ParseError> {
if is_valid_binary_op(self.last_el) ||
self.opened_paren == 0 ||
is_valid_unary_op(self.last_el) {
return Result::Err(ParseError::UnexpectedParen);
}
self.last_el = ')';
self.opened_paren -=1;
if self.opened_paren != 0 {
self.complex_atoms_index -= 1;
}
Result::Ok(())
}
/// Завършва парсването и връща построения израз.
pub fn finish(self) -> Result<Expr, ParseError> {
if self.complex_atoms.is_empty() ||
self.complex_atoms.len() < self.needed_atoms ||
self.opened_paren != 0{
return Result::Err(ParseError::UnexpectedExpr);
}
let mut simple_expr_atoms: Vec<Expr> = Vec::new();
for atom in self.complex_atoms {
let mut sep = SimpleExprParser::new();
for char in atom {
if is_valid_atom(char) {
let _ = sep.push_atom(char);
}
else {
let _ = sep.push_op(char);
}
}
simple_expr_atoms.push(sep.finish().unwrap());
}
let mut curr_expr = simple_expr_atoms[0].clone();
if self.operators.is_empty() {
return Ok(curr_expr);
}
let mut i:usize = 1;
let mut j:usize = 0;
if is_valid_unary_op(self.operators[0]) {
curr_expr = not(curr_expr);
j += 1;
if self.operators.len() == 1 {
return Ok(curr_expr);
}
}
while j < self.operators.len(){
if self.operators[j].eq(&'&') {
if j+1 < self.operators.len() && is_valid_unary_op(self.operators[j + 1]){
curr_expr = and(vec![curr_expr, not(simple_expr_atoms[i].clone())]);
j+=1;
}
else {
curr_expr = and(vec![curr_expr, simple_expr_atoms[i].clone()]);
}
i+=1;
j+=1;
}
else if self.operators[j].eq(&'|') {
if j+1 < self.operators.len() && is_valid_unary_op(self.operators[j + 1]){
curr_expr = or(vec![curr_expr, not(simple_expr_atoms[i].clone())]);
j+=1;
}
else {
curr_expr = or(vec![curr_expr, simple_expr_atoms[i].clone()]);
}
i+=1;
j+=1;
}
else {
curr_expr = not(simple_expr_atoms[i].clone());
i+=1;
j+=1;
}
}
Result::Ok(curr_expr)
}
}
#[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(atom) => {
if truthy.contains(&atom) {
Value::True
} else if falsy.contains(&atom) {
Value::False
} else {
Value::Expr(expr.clone())
}
}
Expr::Not(inner) => {
match eval(inner, truthy, falsy) {
Value::True => Value::False,
Value::False => Value::True,
Value::Expr(inner_expr) => Value::Expr(Expr::Not(Box::new(inner_expr))),
}
}
// Evaluating AND operation
Expr::And(operands) => {
let mut simplified_operands = Vec::new();
let mut is_any_false = false;
for operand in operands {
match eval(operand, truthy, falsy) {
Value::True => {
continue;
}
Value::False => {
is_any_false = true;
break;
}
Value::Expr(sub_expr) => {
simplified_operands.push(sub_expr);
}
}
}
if is_any_false {
Value::False
} else if simplified_operands.is_empty() {
Value::True
} else if simplified_operands.len() == 1 {
Value::Expr(simplified_operands.into_iter().next().unwrap())
} else {
Value::Expr(Expr::And(simplified_operands))
}
}
// Evaluating OR operation
Expr::Or(operands) => {
let mut simplified_operands = Vec::new();
let mut is_any_true = false;
for operand in operands {
match eval(operand, truthy, falsy) {
Value::True => {
is_any_true = true;
break;
}
Value::False => {
continue;
}
Value::Expr(sub_expr) => {
simplified_operands.push(sub_expr);
}
}
}
if is_any_true {
Value::True
} else if simplified_operands.is_empty() {
Value::False
} else if simplified_operands.len() == 1 {
Value::Expr(simplified_operands.into_iter().next().unwrap())
} else {
Value::Expr(Expr::Or(simplified_operands))
}
}
}
}

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

Compiling solution v0.1.0 (/tmp/d20241224-258381-5x65pb/solution)
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.78s
     Running tests/solution_test.rs (target/debug/deps/solution_test-1428e1090729d165)

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

failures:

---- solution_test::test_error_paren_mismatched stdout ----
thread '<unnamed>' panicked at 'assertion failed: matches!(parser.close_paren(), Err(_))', tests/solution_test.rs:331:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'solution_test::test_error_paren_mismatched' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:325:5

---- solution_test::test_paren_nested stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedExpr', src/lib.rs:347:49
thread 'solution_test::test_paren_nested' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedExpr', tests/solution_test.rs:252:5

---- solution_test::test_paren_surrounded stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `Or([Or([Or([Atom('X'), And([Atom('A'), Atom('B')])]), And([Atom('C'), Atom('D')])]), Atom('Y')])`,
 right: `Or([Atom('X'), And([Atom('A'), Atom('B')]), And([Atom('C'), Atom('D')]), Atom('Y')])`', tests/solution_test.rs:238:9
thread 'solution_test::test_paren_surrounded' panicked at 'assertion failed: `(left == right)`
  left: `Or([Or([Or([Atom('X'), And([Atom('A'), Atom('B')])]), And([Atom('C'), Atom('D')])]), Atom('Y')])`,
 right: `Or([Atom('X'), And([Atom('A'), Atom('B')]), And([Atom('C'), Atom('D')]), Atom('Y')])`', tests/solution_test.rs:235: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

---- solution_test::test_parser_multiple_not stdout ----
thread '<unnamed>' panicked at 'index out of bounds: the len is 1 but the index is 1', src/lib.rs:187:38
thread 'solution_test::test_parser_multiple_not' panicked at 'index out of bounds: the len is 1 but the index is 1', tests/solution_test.rs:140:5


failures:
    solution_test::test_error_paren_mismatched
    solution_test::test_paren_nested
    solution_test::test_paren_surrounded
    solution_test::test_parser_multiple_atoms_same_op
    solution_test::test_parser_multiple_not

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

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

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

Милица качи първо решение на 21.12.2024 16:46 (преди 9 месеца)