python - 根據表達式打印真值表

  輸入邏輯表達式,輸出真值表,支持六個基本邏輯運算

最終效果

    輸入合適公式(沒有考慮優先級,只根據括號優先),輸出時會提取其中的元素(比如這裏有A B C),並打印真值表

 算法思路

    求值的一般順序是:帶入每個元素的值,求出式子的真值,所以可以分爲兩塊:1.枚舉元素值(枚舉) 2.根據元素值求式子值(計算

    我認爲這個的難點在於枚舉,我們既要想辦法提取出每一個元素,又要爲其賦值(0和1)

    我們先約定好六個運算符

'''
否.......... !(非)
合取........ &(與)
可兼或...... |(或)
不可兼或.... #
由...可得... >
當且僅當.... =
'''

    並封裝好這個真值表打印器 

class FindTruth:
    '''構造函數'''
    def __init__(self):

    '''輸入'''
    def __In(self):

    '''枚舉'''
    def __Count(self, i):

    '''計算公式結果'''
    def __Sum(self, Str):

    '''配對左右括號'''
    def __Pei(self, cur, Str):
   
    '''運算操作符'''
    def __Add(self, w, a, b = -1):

    '''輸出'''
    def __Out(self):

    一,首先處理枚舉:

    對於提取元素:涉及到字符串的處理以及查重,python提供了便捷的set集合來處理

    對於賦值:比如式子A|B|C,提取出ABC後,一共有從000 到 111,2^3種情況,而N個元素就有2^N種情況,顯然是不能用簡單的循環是實現的,這裏可以用遞歸來處理

        a.利用set提取元素  

            set(字符串)能把字符串裏每個字符提取出來,構成一個集合,而集合是不存在相同字符的,所以這裏完成了篩選重複字符的工作。

            如set('A|B&C') = {'A', 'B', 'C', '|', '&'}

            seta.difference(setb)能讓seta留下於setb集合中不同的元素。

            所以第二句生成了一個篩選掉運算符的元素集合,完成了元素的提取

def __In(self):
    #得到表達式Str
    self.Str = input("input your expression: \n")
    #篩出字母集合
    self.Set = set(self.Str).difference(set("()!&|>=#"))

        b.利用遞歸枚舉賦值

            如何遞歸呢,這裏假如有三個元素A,B,C

            遞歸函數有一個參數 i ,用來指定此次因該枚舉哪一個元素的值,比如 i = 0, 則枚舉A的值。

            首先初始化一個字典用來存儲元素的值:self.dict = {'A':0, 'B':0, 'C': 0}

            第一次:{'A':0, 'B':0, 'C': 0},i = 1,所以先分別給A賦值0和賦值1,並把 i 加一,再調用枚舉函數(也就是遞歸調用自己)。因爲要調用自己兩次,這裏就產生了1*2 = 2個分支:

                分支1:{'A':0, 'B':0, 'C': 0},i = 1

                分支2:{'A':1, 'B':0, 'C': 0},i = 1

            第二次,i = 1,分別給B賦值0和1,在第一次的兩個分支的基礎上產生了2*2 = 4個分支

                分支1:{'A':0, 'B':0, 'C': 0},i = 1 

                    分支11:{'A':0, 'B':0, 'C': 0},i = 2

                    分支12:{'A':0, 'B':1, 'C': 0},i = 2

                分支2:{'A':1, 'B':0, 'C': 0},i = 1

                    分支21:{'A':1, 'B':0, 'C': 0},i = 2

                    分支22:{'A':1, 'B':1, 'C': 0},i = 2

            第三次,同理,給C賦值的時候,會產生總共4*2 = 8個分支,這時候遞歸枚舉也結束了,我們也得到了八個枚舉的結果 

A B C
0 0 0 
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

        實現代碼:

def __Count(self, i):
    '''結束條件:若最後一個元素也完成了枚舉,則 打印 + 運算 '''
    if i == len(self.Lis):
        S = ''
        for l in self.Lis:
            S = S + str(self.Dic[l]) + ' '
        print(S,self.__Sum(self.Str))#調用self__Sum()函數進行運算
        return
    '''不是結尾,則賦值並調用自己,產生兩個分支'''
    self.Dic[self.Lis[i]] = 0
    self.__Count(i+1)
    self.Dic[self.Lis[i]] = 1
    self.__Count(i+1)

    二,再來處理計算 __Sum(self, Str)

    計算的思路很簡單,我們分別設置三個值:

        s :用來存儲當前的終值,初始值設置爲-1

        s0 :用來存儲當前式子或元素的值

        operater :用來存儲目前的操作符 

    然後一個字符一個字符地循環遍歷表達式:

       例:str = 'A | (A & B) & C'   調用 __sum(self, str):

        循環1:字符 = 'A",則s = A的值

        循環2:字符 = '|',則operater = '|'

        循環3:字符 = '(',則s0 = __sum(self, ’括號內的那坨表達式‘),(這樣便可利用遞歸簡單地完成括號的處理)

                     s = (s operator s0),s0 = -1(更新s的值)

        .......

        到最後一個字符結束後,式子的值已經存儲於s中了。

        實現代碼:

#求公式結果
def __Sum(self, Str):
    i = 0 #字符位置
    s = -1#式子真值
    while i < len(Str):
        c = Str[i]
    #單操作符'!'要做特殊的分類處理
        if c == "!":
        #右邊是字母
            if Str[i+1] in self.Set:
                c = Str[i+1]
                i = i + 1
                s0 = self.__Add('!',self.Dic[c])   
        #右邊是左括號
            else:
                end = self.__Pei(i+1, Str)
                s0 = self.__Add('!', self.__Sum(Str[i+1:end+1]))
                i = end
    #字母
        elif c in self.Set:
            s0 = self.Dic[c]
    #其它運算符
        elif c in set("&|>=#"):
            operat = c
    #左括號
        elif c == '(':
            end = self.__Pei(i, Str)
            s0 = self.__Sum(Str[i+1:end])
            i = end
    #運算結果
        if s == -1:
            s = s0
            s0 = -1
        elif operat != 0 and s0 != -1:
            s1 = s
            s = self.__Add(operat, s, s0)
            operat = 0
            s0 = -1
        i = i + 1
    return s
#配對左右括號
def __Pei(self, cur, Str):
    kflag = 1  # 左括號的數目
    while not kflag == 0:
        cur = cur + 1
        if Str[cur] == '(':
            kflag = kflag + 1
        elif Str[cur] == ')':
            kflag = kflag - 1
    return cur    
#運算操作
def __Add(self, operator, a, b = -1):#b默認爲-1時,表示是單操作符號' ! '
    if operator == '!':
        boo = not a
    elif operator == '&':
        boo = a and b
    elif operator == '|':
        boo = a or b
    elif operator == '#':
        boo = ((not a) or (not b)) and (a or b)
    elif operator == '>':
        boo = (not a) or b
    elif operator == '=':
        boo = ((not a) and (not b)) or (a and b)
    else:
        print("there is no such operator")
    if boo:
        return 1
    else:
        return 0

完整代碼: 

# -*- coding: utf-8 -*-
'''
否.......... !
合取........ &
可兼或...... |
不可兼或.... #
由...可得... >
當且僅當.... =
'''
class FindTruth:
    def __init__(self):
        #存儲字母及其真值
        self.Dic = {}
        self.Lis = []
        #輸入表達式
        self.__In()
        #輸出真值表
        self.__Out()
    #輸入
    def __In(self):
        #得到表達式Str
        self.Str = input("input your expression: \n")
        #篩出字母集合
        self.Set = set(self.Str).difference(set("()!&|>=#"))
    #求公式結果
    def __Sum(self, Str):
        i = 0 #字符位置
        s = -1#式子真值
        while i < len(Str):
            c = Str[i]
        #單操作符'!'要做特殊的分類處理
            if c == "!":
            #右邊是字母
                if Str[i+1] in self.Set:
                    c = Str[i+1]
                    i = i + 1
                    s0 = self.__Add('!',self.Dic[c])   
            #右邊是左括號
                else:
                    end = self.__Pei(i+1, Str)
                    s0 = self.__Add('!', self.__Sum(Str[i+1:end+1]))
                    i = end
        #字母
            elif c in self.Set:
                s0 = self.Dic[c]
        #其它運算符
            elif c in set("&|>=#"):
                operat = c
        #左括號
            elif c == '(':
                end = self.__Pei(i, Str)
                s0 = self.__Sum(Str[i+1:end])
                i = end
        #運算結果
            if s == -1:
                s = s0
                s0 = -1
            elif operat != 0 and s0 != -1:
                s1 = s
                s = self.__Add(operat, s, s0)
                operat = 0
                s0 = -1
            i = i + 1
        return s
    #配對左右括號
    def __Pei(self, cur, Str):
        kflag = 1  # 左括號的數目
        while not kflag == 0:
            cur = cur + 1
            if Str[cur] == '(':
                kflag = kflag + 1
            elif Str[cur] == ')':
                kflag = kflag - 1
        return cur    
    #運算操作
    def __Add(self, operator, a, b = -1):#b默認爲-1時,表示是單操作符號' ! '
        if operator == '!':
            boo = not a
        elif operator == '&':
            boo = a and b
        elif operator == '|':
            boo = a or b
        elif operator == '#':
            boo = ((not a) or (not b)) and (a or b)
        elif operator == '>':
            boo = (not a) or b
        elif operator == '=':
            boo = ((not a) and (not b)) or (a and b)
        else:
            print("there is no such operator")
        if boo:
            return 1
        else:
            return 0
    #輸出
    def __Out(self):
        #將字母放入dict和List
        S = ''
        for c in self.Set:
            self.Dic[c] = 0
            self.Lis.append(c)
            S = S + c + ' '
        print(S, self.Str)
        self.__Count(0)
    #構造2^n的序列
    def __Count(self, i):
        #是結尾,打印 + 運算
        if i == len(self.Lis):
            S = ''
            for l in self.Lis:
                S = S + str(self.Dic[l]) + ' '
            print(S,self.__Sum(self.Str))
            return
        #不是結尾,遞歸賦值
        self.Dic[self.Lis[i]] = 0
        self.__Count(i+1)
        self.Dic[self.Lis[i]] = 1
        self.__Count(i+1)

if __name__ == '__main__':
    F = FindTruth()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章