【算法实战】每日一题:如何实现一个简单的逻辑表达式求值器

题目

如何实现一个简单的逻辑表达式求值器

要求实现一个程序,该程序能够接收一个由逻辑运算符(“or”、“and”、“not”)和逻辑值(“true”、“false”)组成的字符串表达式,然后计算并输出这个表达式的结果。

核心思路(函数级)

  1. 操作符判断 (is_op): 定义一个函数来判断给定的字符串是否为逻辑操作符(“or”、“and” 或 “not”)。

  2. 操作符优先级 (priority): 定义一个函数来确定不同逻辑操作符的优先级,以便于按正确的顺序执行操作。

  3. 操作符处理 (process_op): 定义一个函数来执行逻辑操作。根据操作符(“not”、“and” 或 “or”)和操作数(“true” 或 “false”),更新栈顶元素的值。

  4. 表达式求值 (evaluate): 定义一个函数来计算整个逻辑表达式的值。使用两个栈(一个用于操作数,一个用于操作符)来处理表达式,并根据操作符的优先级和结合性来计算最终结果。

  5. 表达式合法性检查 (check): 定义一个函数来验证逻辑表达式是否符合语法规则,例如,不能以操作符开始或结束,“not” 后面必须跟操作数等。

  6. 主函数 (main): 主函数负责读取用户输入的逻辑表达式,将其分割成操作符和操作数的列表,然后调用 check 函数检查表达式的合法性。如果表达式合法,调用 evaluate 函数计算并打印结果;如果不合法,打印错误信息。

函数流程

  1. main() 函数: 程序执行从 main 函数开始。

  2. check(s, cnt): 然后,main 函数调用 check 函数,传入分割后的列表 s 和计数 cnt,以检查表达式的合法性。

  3. evaluate(s, cnt): 如果 check 函数返回 Truemain 函数接着调用 evaluate 函数来计算逻辑表达式的值。

  4. is_op(s[i]): 在 evaluate 函数内部,对于每个元素 s[i],首先调用 is_op 函数来判断它是否是一个操作符。

  5. priority(op): 如果是操作符,evaluate 函数会调用 priority 函数来获取操作符的优先级。

  6. process_op(st, op): 在操作符的优先级确定后,如果需要,evaluate 函数将调用 process_op 函数来执行栈顶的操作数和操作符的逻辑运算。

  7. 循环处理: evaluate 函数中的循环会重复处理输入的表达式,直到所有的操作符都被处理完毕。

  8. 返回结果: 最终,evaluate 函数返回计算得到的逻辑表达式的值。

  9. 打印结果: main 函数接收到 evaluate 函数返回的结果,并将其打印出来。

  10. 错误处理: 如果 check 函数返回 False,表示表达式不合法,main 函数将打印错误信息。

伪代码

初始化一个名为cnt的变量为0,用于计数输入的逻辑表达式中的元素数量。

定义一个名为is_op的函数,它接受一个字符串参数s。这个函数检查s是否是"or"、"and"或"not"中的任意一个。如果是,函数返回True;否则,返回False。

定义一个名为priority的函数,它接受一个操作符参数op。这个函数根据op是"or"、"and"还是"not",返回相应的优先级值1、2或3。

定义一个名为process_op的函数,它接受一个栈st和一个操作符op作为参数。这个函数执行如下操作:
  - 弹出栈st顶部的元素r。
  - 如果op是"not",根据r的值("true"或"false"),将相反的值压入栈st。
  - 如果op是"and",先弹出栈st顶部的元素l,然后将l与r进行逻辑与操作,将结果压入栈st。
  - 如果op是"or",先弹出栈st顶部的元素l,然后将l与r进行逻辑或操作,将结果压入栈st。

定义一个名为evaluate的函数,它接受一个字符串列表s和一个计数器cnt作为参数。这个函数执行如下操作:
  - 初始化两个空栈,分别用于存储操作数和操作符。
  - 遍历列表s中的每个元素:
    - 如果元素是操作符,根据优先级和操作符类型,从操作符栈中弹出相应的操作符,并使用process_op函数进行处理。
    - 如果元素不是操作符(即是一个操作数),将它压入操作数栈。
  - 当所有元素都被处理后,弹出操作符栈中的剩余操作符,并进行处理,直到栈为空。
  - 返回操作数栈顶部的元素,这是逻辑表达式计算的结果。

定义一个名为check的函数,它接受一个字符串列表s和一个计数器cnt作为参数。这个函数检查逻辑表达式的合法性,包括:
  - 如果cnt为0,即没有输入任何元素,返回False。
  - 检查表达式的第一个元素和最后一个元素,确保它们不是操作符。
  - 检查"not"操作符后面是否跟有非法的操作符。
  - 如果发现任何非法模式,设置标志变量flag为False并返回。

定义一个名为main的函数,作为程序的入口点:
  - 初始化一个空列表s,用于存储分割后的逻辑表达式。
  - 读取用户输入的整个逻辑表达式,清除首尾空格,并分割成单词列表,然后更新cnt为列表的长度。
  - 调用check函数检查逻辑表达式的合法性。
  - 如果表达式合法,调用evaluate函数计算表达式的值,并打印结果。
  - 如果表达式不合法,打印"error"。



CODE

cnt = 0

# 定义一个函数,用于判断字符串是否为操作符
def is_op(s):
    # 如果s等于"or"、"and"或"not"中的任意一个,那么表达式的结果为True;否则,结果为False。
    return s == "or" or s == "and" or s == "not"


# 定义一个函数,用于计算操作符的优先级
def priority(op):
    if op == "or":
        return 1
    if op == "and":
        return 2
    if op == "not":
        return 3
    return -1


# 定义一个函数,用于处理操作符
def process_op(st, op):
    """
    如果运算符是"not",那么将栈顶元素r与"true"进行比较,如果相等,则将"false"压入栈中;否则将"true"压入栈中。
    如果运算符是"and",那么先弹出栈顶元素l,然后将l与r进行比较,如果都为"true",则将"true"压入栈中;否则将"false"压入栈中。
    如果运算符是"or",那么先弹出栈顶元素l,然后将l与r进行比较,如果都为"false",则将"false"压入栈中;否则将"true"压入栈中。
    栈(stack)主要用于存储逻辑运算的 operands(操作数)
    """
    # 栈
    r = st.pop()
    if op == "not":
        st.append("false" if r == "true" else "true")
    elif op == "and":
        l = st.pop()
        st.append("true" if r == "true" and l == "true" else "false")
    elif op == "or":
        l = st.pop()
        st.append("false" if r == "false" and l == "false" else "true")


# 定义一个函数,用于计算表达式的值
def evaluate(s, cnt):
    """
    evaluate(s, cnt): 这个函数接受两个参数,s 是一个字符串列表,其中包含了逻辑表达式中的运算符和操作数;cnt 是列表 s 中元素的数量。

    将一个字符串s中的运算符和操作数进行优先级计算,最终得到一个计算结果。
    遍历字符串s中的每个字符,判断其是否为运算符。如果是运算符,则进行以下操作:
    a. 获取当前运算符cur_op。 b. 当op列表不为空,且op列表中的最后一个元素的优先级大于等于cur_op时,弹出op列表中的最后一个元素,并进行计算。 c. 如果cur_op不为"not",则当op列表中的最后一个元素为"not"时,弹出op列表中的最后一个元素,并进行计算。 d. 将cur_op添加到op列表中。

    """
    st = []
    op = []

    for i in range(cnt):
        #  a. 如果是操作数,就获取当前操作符 cur_op。
        if is_op(s[i]):
            cur_op = s[i]
            """步骤 b 是一个通用的操作符优先级处理,它适用于所有二元操作符("and" 和 "or")。
            步骤 c 是一个特殊情况处理,专门针对 "not" 操作符,因为 "not" 是一个单目操作符,它的处理逻辑与二元操作符不同。
            """
            #  b. 当操作符栈 op 不为空,并且栈顶操作符的优先级大于等于 cur_op 的优先级时,从 op 栈中弹出操作符,并使用 process_op 函数进行计算。
            # 空了表示之前的都完成计算了可以继续引入运算符
            while op and priority(op[-1]) >= priority(cur_op):
                process_op(st, op.pop())
            #  c. 如果 cur_op 不是 "not",并且 op 栈不为空且栈顶为 "not",则弹出 "not" 并进行计算。(NOT优先级高)
            if cur_op != "not":
                while op and op[-1] == "not":
                    process_op(st, op.pop())
            # 其实这里bc可以合并的
            #  d. 将 cur_op 添加到操作符栈 op 中。
            op.append(cur_op)
        else:
            st.append(s[i])
    while op:
        process_op(st, op.pop())
    return st[0]


# 定义一个函数,用于检查表达式是否合法
def check(s, cnt):
    """
        s 是一个字符串列表,代表逻辑表达式中的各个元素
        cnt 是列表 s 中元素的数量。
        确保:表达式不能以 "or" 或 "and" 开头。
    表达式的最后一个元素不能是 "or", "and", 或 "not"。
    "not" 操作符后面必须是 "true" 或 "false",而不能是另一个操作符。
    """
    # cnt为0的处理
    if cnt == 0:
        return False
    
    flag = True

    # 如果表达式的第一个元素是 "or" 或 "and",或者最后一个元素是 "or", "and", 或 "not",则表达式不合法。在这种情况下,将 flag 设置为 False 并返回。
    if (s[0] == "or" or s[0] == "and" or s[cnt - 1] == "or" or s[cnt - 1] == "and") or (
        s[cnt - 1] == "not"
    ):
        flag = False
        return flag

    # 如果遇到 "not" 操作符(s[i] == "not"),则检查其后一个元素(s[i + 1])是否为 "or" 或 "and"。如果是,那么这个表达式是非法的,因为 "not" 后面应该跟一个逻辑值("true" 或 "false"),而不是另一个操作符。在这种情况下,将 flag 设置为 False 并返回。
    for i in range(cnt):
        if s[i] == "not" and (i + 1 < cnt and (s[i + 1] == "or" or s[i + 1] == "and")):
            flag = False
            return flag
    return flag


# 主函数
def main():
    global cnt
    s = []  # 使用空列表来存储输入的逻辑表达式

    # 一次性读取整个输入表达式
    expr = input().strip()
    # 分割表达式为单词列表,这里假设输入的逻辑表达式由空格分隔
    s = expr.split()
    cnt = len(s)  # 更新计数器为输入表达式的长度

    if check(s, cnt):
        print(evaluate(s, cnt))
    else:
        print("error")

if __name__ == "__main__":
    main()



END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_千思_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值