1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        如何用有限狀態(tài)機(jī)識(shí)別地址的有效性?

        共 6782字,需瀏覽 14分鐘

         ·

        2021-09-10 14:09

        在收發(fā)快遞填寫地址的時(shí)候,我們會(huì)經(jīng)常手動(dòng)輸入地址讓程序智能識(shí)別,標(biāo)準(zhǔn)的地址比如,xx省xx市xx縣/區(qū)xx路xx號(hào),不過有時(shí)候也可以簡單寫:xx市xx縣/區(qū)xx路xx號(hào),或者xx省xx縣/區(qū)xx路xx號(hào),或者xx市xx路xx號(hào)。

        但是有些就不是合法的地址了,比如 xx省xx街道xx號(hào),或者 xx市xx省xx區(qū)xx號(hào)。

        那么問題來了,如何識(shí)別一個(gè)地址是否有效,確切的講,如何編程識(shí)別一個(gè)中國地址是否有效?

        雖然我們大腦可以一眼識(shí)別,但是讓計(jì)算器去識(shí)別,可以不是一件容易的事,根本原因在于地址的描述雖然看上去簡單,但是它依然是比較復(fù)雜的上下文有關(guān)的文法。

        比如 “上海市北京東路 xx 號(hào),南京市北京東路 xx 號(hào)”,掃描到北京東路時(shí),它后面的門牌號(hào)是否構(gòu)成正確的地址要看上下文,即城市名。

        所幸的是,地址的上下文比較簡單,是有限的,雖然我們可以暴力窮舉所有省、市、區(qū)、街道。但有效的方法還是有限狀態(tài)機(jī)。

        每一個(gè)有限狀態(tài)機(jī)都有一個(gè)開始狀態(tài)和一個(gè)終止?fàn)顟B(tài),以及若干中間狀態(tài),每一條弧上帶著一個(gè)狀態(tài)進(jìn)入下一個(gè)狀態(tài)的條件,比如在上圖中當(dāng)前的狀態(tài)如果是省,如果遇到下一個(gè)詞組和區(qū)有關(guān)就進(jìn)入?yún)^(qū),如果遇到下一個(gè)詞組和城市有關(guān)那么就進(jìn)入市。

        如果一條地址能從狀態(tài)機(jī)的開始狀態(tài),經(jīng)過狀態(tài)機(jī)的若干中間狀態(tài),最終走到終止?fàn)顟B(tài),則這條地址有效,否則無效。

        比如 xx市xx省xx區(qū)xx號(hào) 就是無效地址,無法從市走到省。

        現(xiàn)在我們通過一個(gè)簡單的優(yōu)先狀態(tài)機(jī)來實(shí)現(xiàn),代碼有注釋,很容易看懂

        from enum import Enum

        def isAddress(address: str) -> bool:

            #定義狀態(tài)
            State = Enum("State", [
                "STATE_INITIAL"#開始
                "STATE_PROVINCE"# 省
                "STATE_CITY"# 市
                "STATE_AREA"# 區(qū) / 縣
                "STATE_STREET"# 街道
                "STATE_NUM"#號(hào)
                "STATE_END"#結(jié)束
                "STATE_ILLEGAL"#錯(cuò)誤狀態(tài)
            ])

            def toAddressType(addr_slice : str) -> State:
                if "省" in addr_slice:
                    return State.STATE_PROVINCE
                elif "市" in addr_slice:
                    return State.STATE_CITY
                elif "區(qū)" in addr_slice or "縣" in addr_slice:
                    return State.STATE_AREA
                elif "路" in addr_slice or "街道" in addr_slice:
                    return State.STATE_STREET
                elif "號(hào)" in addr_slice:
                    return State.STATE_NUM
                else:
                    return State.STATE_ILLEGAL
            
            #定義狀態(tài)轉(zhuǎn)移
            
            transfer = {

                #開始可以轉(zhuǎn)為 省或市
                State.STATE_INITIAL: {
                    State.STATE_PROVINCE, 
                    State.STATE_CITY,
                },

                #省可以轉(zhuǎn) 市或區(qū)縣
                State.STATE_PROVINCE:{
                    State.STATE_CITY,
                    State.STATE_AREA,
                },

                #市可以轉(zhuǎn)區(qū)或街道
                State.STATE_CITY: {
                    State.STATE_AREA,
                    State.STATE_STREET,
                },

                #區(qū)縣可以轉(zhuǎn)街道
                State.STATE_AREA: {
                    State.STATE_STREET,
                },

                #街道可以轉(zhuǎn)號(hào)或終止
                State.STATE_STREET: {
                    State.STATE_NUM,
                    State.STATE_END,
                },

                #號(hào)只能轉(zhuǎn)終止
                State.STATE_NUM: {
                    State.STATE_END,
                },
            }

            st = State.STATE_INITIAL
            for ch in address:
                current_state = toAddressType(ch)
                if current_state not in transfer[st]:
                    return False
                st = current_state 

            return st in [State.STATE_STREET, State.STATE_NUM,State.STATE_END]

        if __name__ == '__main__':
            address1 = ["江蘇省","蘇州市""吳中區(qū)""中山北路""208號(hào)"]
            address2 = ["蘇州市","吳中區(qū)""中山北路""208號(hào)"]
            address3 = ["蘇州市","吳江區(qū)""中山北路""208號(hào)"]
            address4 = ["蘇州市","吳江區(qū)","208號(hào)"]
            address5 = ["蘇州市","中山北路"]

            assert isAddress(address1)
            assert isAddress(address2)
            assert isAddress(address3)
            assert isAddress(address5)
            assert isAddress(address4) == False

        這里沒有對(duì)整個(gè)地址字符串進(jìn)行分詞,而是直接將地址寫成了列表的形式,主要為了說明狀態(tài)機(jī)的實(shí)現(xiàn)和應(yīng)用,上述代碼僅能從格式上保證地址是有效的,并不能確保地址真實(shí)有效,如果要判斷是真實(shí)有效的,那就需要將全國所有的省、市、區(qū)縣、街道建立一個(gè) hash 表,門牌號(hào)可以用范圍表示,再進(jìn)行狀態(tài)轉(zhuǎn)移判斷。

        上述代碼的 transfer 就是一個(gè) hash 表,相當(dāng)于把所有正確轉(zhuǎn)移的情況都窮舉了一遍,它窮盡了在任何一種情況下,對(duì)應(yīng)任何的輸入,需要轉(zhuǎn)義的狀態(tài)。

        最后的話

        本文分享了如何實(shí)現(xiàn)一個(gè)簡單的有限狀態(tài)機(jī),代碼比較通用,前文這個(gè)編程題,讓人欲罷不能也是套用這個(gè)代碼實(shí)現(xiàn)的,如果對(duì)你有所幫助,還請(qǐng)點(diǎn)贊、關(guān)注支持,贈(zèng)人在看,手留余香。

        附有限狀態(tài)機(jī)的開源實(shí)現(xiàn):

        1. django-fsm[1]
        2. python-state-machine[2]

        關(guān)注我,每天學(xué)習(xí)一個(gè) Python 小技術(shù)。

        參考資料

        [1]

        django-fsm: https://github.com/viewflow/django-fsm

        [2]

        python-state-machine: https://github.com/jtushman/state_machine


        瀏覽 62
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日韩 欧美 国产精品 | 免费观看一级毛片 | 欧美国产黄片 | 黄色。****yor | 亚洲免费观看高清视频 | 麻豆成人免费 | 免费的毛片 | 我把女的日出了白浆 | 错一题c10下 | 在线观看国产www |