99999久久久久久亚洲,欧美人与禽猛交狂配,高清日韩av在线影院,一个人在线高清免费观看,啦啦啦在线视频免费观看www

熱線(xiàn)電話(huà):13121318867

登錄
首頁(yè)精彩閱讀用Python創(chuàng)建聲明性迷你語(yǔ)言的教程
用Python創(chuàng)建聲明性迷你語(yǔ)言的教程
2018-08-19
收藏

用Python創(chuàng)建聲明性迷你語(yǔ)言的教程

大多數(shù)程序員考慮編程時(shí),他們都要設(shè)想用于編寫(xiě)應(yīng)用程序的 命令式樣式和技術(shù)。最受歡迎的通用編程語(yǔ)言(包括 Python 和其它面向?qū)ο蟮恼Z(yǔ)言)在樣式上絕大多數(shù)都是命令式的。另一方面,也有許多編程語(yǔ)言是 聲明性樣式,包括函數(shù)語(yǔ)言和邏輯語(yǔ)言,還包括通用語(yǔ)言和專(zhuān)用語(yǔ)言。

讓我們列出幾個(gè)屬于各個(gè)種類(lèi)的語(yǔ)言。許多讀者已經(jīng)使用過(guò)這些工具中的許多工具,但不見(jiàn)得考慮過(guò)它們之間的種類(lèi)差別。Python、C、C++、Java、Perl、Ruby、Smalltalk、Fortran、Basic 和 xBase 都是簡(jiǎn)單的命令式編程語(yǔ)言。其中,一些是面向?qū)ο蟮?,但那只是組織代碼和數(shù)據(jù)的問(wèn)題,而非基本編程樣式的問(wèn)題。使用這些語(yǔ)言,您 命令程序執(zhí)行指令序列:把某些數(shù)據(jù) 放入(put)變量中;從變量中 獲?。╢etch)數(shù)據(jù); 循環(huán)(loop)一個(gè)指令塊 直到(until)滿(mǎn)足了某些條件; 如果(if)某個(gè)命題為 true,那么就進(jìn)行某些操作。所有這些語(yǔ)言的一個(gè)妙處在于:便于用日常生活中熟悉的比喻來(lái)考慮它們。日常生活都是由做事、選擇、再做另一件事所組成的,期間或許會(huì)使用一些工具??梢院?jiǎn)單地將運(yùn)行程序的計(jì)算機(jī)想象成廚師、瓦匠或汽車(chē)司機(jī)。

諸如 Prolog、Mercury、SQL、XSLT 這樣的語(yǔ)言、EBNF 語(yǔ)法和各種格式的真正配置文件,都 聲明某事是這種情況,或者應(yīng)用了某些約束。函數(shù)語(yǔ)言(比如 Haskell、ML、Dylan、Ocaml 和 Scheme)與此相似,但是它們更加強(qiáng)調(diào)陳述編程對(duì)象(遞歸、列表,等等)之間的內(nèi)部(函數(shù))關(guān)系。我們的日常生活(至少在敘事質(zhì)量方面)沒(méi)有提供對(duì)這些語(yǔ)言的編程構(gòu)造的直接模擬。然而,對(duì)于那些可以用這些語(yǔ)言進(jìn)行描述的問(wèn)題來(lái)說(shuō),聲明性描述 遠(yuǎn)遠(yuǎn)比命令式解決方案來(lái)得簡(jiǎn)明且不易出錯(cuò)。例如,請(qǐng)研究下面這個(gè)線(xiàn)性方程組:
清單 1. 線(xiàn)性方程式系統(tǒng)樣本    
10x + 5y - 7z + 1 = 0
17x + 5y - 10z + 3 = 0
5x - 4y + 3z - 6 = 0

這是個(gè)相當(dāng)漂亮的說(shuō)明對(duì)象(x、y 和 z)之間幾個(gè)關(guān)系的簡(jiǎn)單表達(dá)式。在現(xiàn)實(shí)生活中您可能會(huì)用不同的方式求出這些答案,但是實(shí)際上用筆和紙“求解 x”很煩,而且容易出錯(cuò)。從調(diào)試角度來(lái)講,用 Python 編寫(xiě)求解步驟或許會(huì)更糟糕。

Prolog 是與邏輯或數(shù)學(xué)關(guān)系密切的語(yǔ)言。使用這種語(yǔ)言,您只要編寫(xiě)您知道是正確的語(yǔ)句,然后讓?xiě)?yīng)用程序?yàn)槟贸鼋Y(jié)果。語(yǔ)句不是按照特定的順序構(gòu)成的(和線(xiàn)性方程式一樣,沒(méi)有順序),而且您(程序員或用戶(hù))并不知道得出的結(jié)果都采用了哪些步驟。例如:
清單 2. family.pro Prolog 樣本    
/* Adapted from sample at:
<http://www.engin.umd.umich.edu/CIS/course.des/cis479/prolog/>
This app can answer questions about sisterhood & love, e.g.:
# Is alice a sister of harry?
?-sisterof( alice, harry )
# Which of alice' sisters love wine?
?-sisterof( X, alice ), love( X, wine)
*/
sisterof( X, Y ) :- parents( X, M, F ),
          female( X ),
          parents( Y, M, F ).
parents( edward, victoria, albert ).
parents( harry, victoria, albert ).
parents( alice, victoria, albert ).
female( alice ).
loves( harry, wine ).
loves( alice, wine ).

它和 EBNF(擴(kuò)展巴科斯范式,Extended Backus-Naur Form)語(yǔ)法聲明并不完全一樣,但是實(shí)質(zhì)相似。您可以編寫(xiě)一些下面這樣的聲明:
清單 3. EBNF 樣本    
word    := alphanums, (wordpunct, alphanums)*, contraction?
alphanums  := [a-zA-Z0-9]+
wordpunct  := [-_]
contraction := "'", ("clock"/"d"/"ll"/"m"/"re"/"s"/"t"/"ve")

如果您遇到一個(gè)單詞而想要表述其看上去 可能會(huì)是什么,而實(shí)際上又不想給出如何識(shí)別它的序列指令,上面便是個(gè)簡(jiǎn)練的方法。正則表達(dá)式與此相似(并且事實(shí)上它能夠滿(mǎn)足這種特定語(yǔ)法產(chǎn)品的需要)。

還有另一個(gè)聲明性示例,請(qǐng)研究描述有效 XML 文檔方言的文檔類(lèi)型聲明:
清單 4. XML 文檔類(lèi)型聲明    
<!ELEMENT dissertation (chapter+)>
<!ELEMENT chapter (title, paragraph+)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT paragraph (#PCDATA | figure)+>
<!ELEMENT figure EMPTY>

和其它示例一樣,DTD 語(yǔ)言不包含任何有關(guān)如何識(shí)別或創(chuàng)建有效 XML 文檔的指令。它只描述了如果文檔存在,那它會(huì)是怎么樣的。聲明性語(yǔ)言采用虛擬語(yǔ)氣。
Python 作為解釋器 vs Python 作為環(huán)境

Python 庫(kù)可以通過(guò)兩種截然不同的方式中的一種來(lái)利用聲明性語(yǔ)言?;蛟S更為常用的技術(shù)是將非 Python 聲明性語(yǔ)言作為數(shù)據(jù)來(lái)解析和處理。應(yīng)用程序或庫(kù)可以讀入外部來(lái)源(或者是內(nèi)部定義的但只用作“blob”的字符串),然后指出一組要執(zhí)行的命令式步驟,這些步驟在某種形式上與那些外部聲明是一致的。本質(zhì)上,這些類(lèi)型的庫(kù)是“數(shù)據(jù)驅(qū)動(dòng)的”系統(tǒng);聲明性語(yǔ)言和 Python 應(yīng)用程序執(zhí)行或利用其聲明的操作之間有著概念和范疇差別。事實(shí)上,相當(dāng)普遍的一點(diǎn)是,處理那些相同聲明的庫(kù)也被用來(lái)實(shí)現(xiàn)其它編程語(yǔ)言。

上面給出的所有示例都屬于第一種技術(shù)。庫(kù) PyLog 是 Prolog 系統(tǒng)的 Python 實(shí)現(xiàn)。它讀取像樣本那樣的 Prolog 數(shù)據(jù)文件,然后創(chuàng)建 Python 對(duì)象來(lái)對(duì) Prolog 聲明 建模。EBNF 樣本使用專(zhuān)門(mén)變體 SimpleParse ,這是一個(gè) Python 庫(kù),它將這些聲明轉(zhuǎn)換成可以被 mx.TextTools 所使用的狀態(tài)表。 mx.TextTools 自身是 Python 的擴(kuò)展庫(kù),它使用底層 C 引擎來(lái)運(yùn)行存儲(chǔ)在 Python 數(shù)據(jù)結(jié)構(gòu)中的代碼,但與 Python 本質(zhì)上幾乎沒(méi)什么關(guān)系。對(duì)于這些任務(wù)而言,Python 是極佳的 粘合劑,但是粘合在一起的語(yǔ)言與 Python 差別很大。而且,大多數(shù) Prolog 實(shí)現(xiàn)都不是用 Python 編寫(xiě)的,這和大多數(shù) EBNF 解析器一樣。

DTD 類(lèi)似于其它示例。如果您使用象 xmlproc 這樣的驗(yàn)證解析器,您可以利用 DTD 來(lái)驗(yàn)證 XML 文檔的方言。但是 DTD 的語(yǔ)言并不是 Python 式的, xmlproc 只將它用作需要解析的數(shù)據(jù)。而且,已經(jīng)用許多編程語(yǔ)言編寫(xiě)過(guò) XML 驗(yàn)證解析器。XSLT 轉(zhuǎn)換與此相似,也不是特定于 Python 的,而且像 ft.4xslt 這樣的模塊只將 Python 用作“粘合劑”。

雖然上面的方法和上面所提到的工具(我一直都在使用)都沒(méi)什么 不對(duì),但如果 Python 本身是聲明性語(yǔ)言的話(huà),那么它可能會(huì)更精妙,而且某些方面會(huì)表達(dá)得更清晰。如果沒(méi)有其它因素的話(huà),有助于此的庫(kù)不會(huì)使程序員在編寫(xiě)一個(gè)應(yīng)用程序時(shí)考慮是否采用兩種(或更多)語(yǔ)言。有時(shí),依靠 Python 的自省能力來(lái)實(shí)現(xiàn)“本機(jī)”聲明,既簡(jiǎn)單又管用。

自省的魔力

解析器 Spark 和 PLY 讓用戶(hù) 用 Python 來(lái)聲明 Python 值,然后使用某些魔法來(lái)讓 Python 運(yùn)行時(shí)環(huán)境進(jìn)行解析配置。例如,讓我們研究一下與前面 SimpleParse 語(yǔ)法等價(jià)的 PLY 語(yǔ)法。 Spark 類(lèi)似于下面這個(gè)示例:
清單 5. PLY 樣本    
tokens = ('ALPHANUMS','WORDPUNCT','CONTRACTION','WHITSPACE')
t_ALPHANUMS = r"[a-zA-Z0-0]+"
t_WORDPUNCT = r"[-_]"
t_CONTRACTION = r"'(clock|d|ll|m|re|s|t|ve)"
def t_WHITESPACE(t):
  r"\s+"
  t.value = " "
  return t
import lex
lex.lex()
lex.input(sometext)
while 1:
  t = lex.token()
  if not t: break

我已經(jīng)在我即將出版的書(shū)籍 Text Processing in Python 中編寫(xiě)了有關(guān) PLY 的內(nèi)容,并且在本專(zhuān)欄文章中編寫(xiě)了有關(guān) Spark 的內(nèi)容(請(qǐng)參閱 參考資料以獲取相應(yīng)鏈接)。不必深入了解庫(kù)的詳細(xì)信息,這里您應(yīng)當(dāng)注意的是:正是 Python 綁定本身配置了解析(在這個(gè)示例中實(shí)際是詞法分析/標(biāo)記化)。 PLY 模塊在 Python 環(huán)境中運(yùn)行以作用于這些模式聲明,因此就正好非常了解該環(huán)境。

PLY如何得知它自己做什么,這涉及到一些非常奇異的 Python 編程。起初,中級(jí)程序員會(huì)發(fā)現(xiàn)可以查明 globals() 和 locals() 字典的內(nèi)容。如果聲明樣式略有差異的話(huà)就好了。例如,假想代碼更類(lèi)似于這樣:
清單 6. 使用導(dǎo)入的模塊名稱(chēng)空間
    
import basic_lex as _
_.tokens = ('ALPHANUMS','WORDPUNCT','CONTRACTION')
_.ALPHANUMS = r"[a-zA-Z0-0]+"
_.WORDPUNCT = r"[-_]"
_.CONTRACTION = r"'(clock|d|ll|m|re|s|t|ve)"
_.lex()

這種樣式的聲明性并不差,而且可以假設(shè) basic_lex 模塊包含類(lèi)似下面這樣的簡(jiǎn)單內(nèi)容:
清單 7. basic_lex.py    
def lex():
  for t in tokens:
    print t, '=', globals()[t]

這會(huì)產(chǎn)生:    
% python basic_app.py
ALPHANUMS = [a-zA-Z0-0]+
WORDPUNCT = [-_]
CONTRACTION = '(clock|d|ll|m|re|s|t|ve)

PLY 設(shè)法使用堆棧幀信息插入了導(dǎo)入模塊的名稱(chēng)空間。例如:
清單 8. magic_lex.py    
import sys
try: raise RuntimeError
except RuntimeError:
  e,b,t = sys.exc_info()
  caller_dict = t.tb_frame.f_back.f_globals
def lex():
  for t in caller_dict['tokens']:
    print t, '=', caller_dict['t_'+t]

這產(chǎn)生了與 basic_app.py 樣本所給輸出一樣的輸出,但是具有使用前面 t_TOKEN 樣式的聲明。

實(shí)際的 PLY 模塊中要比這更神奇。我們看到用模式 t_TOKEN 命名的標(biāo)記實(shí)際上可以是包含了正則表達(dá)式的字符串,或是包含了正則表達(dá)式文檔字符串和操作代碼的函數(shù)。某些類(lèi)型檢查允許以下多態(tài)行為:
清單 9. polymorphic_lex    
# ...determine caller_dict using RuntimeError...
from types import *
def lex():
  for t in caller_dict['tokens']:
    t_obj = caller_dict['t_'+t]
    if type(t_obj) is FunctionType:
      print t, '=', t_obj.__doc__
    else:
      print t, '=', t_obj

顯然,相對(duì)于用來(lái)玩玩的示例而言,真正的 PLY 模塊用這些已聲明的模式可以做更有趣的事,但是這些示例演示了其中所涉及的一些技術(shù)。

繼承的魔力

讓支持庫(kù)到處插入并操作應(yīng)用程序的名稱(chēng)空間,這會(huì)啟用精妙的聲明性樣式。但通常,將繼承結(jié)構(gòu)和自省一起使用會(huì)使靈活性更佳。

模塊 gnosis.xml.validity 是用來(lái)創(chuàng)建直接映射到 DTD 產(chǎn)品的類(lèi)的框架。任何 gnosis.xml.validity 類(lèi) 只能用符合 XML 方言有效性約束的參數(shù)進(jìn)行實(shí)例化。實(shí)際上,這并不十分正確;當(dāng)只存在一種明確的方式可將參數(shù)“提升”成正確類(lèi)型時(shí),模塊也可從更簡(jiǎn)單的參數(shù)中推斷出正確類(lèi)型。

由于我已經(jīng)編寫(xiě)了 gnosis.xml.validity 模塊,所以我傾向于思考其用途自身是否有趣。但是對(duì)于本文,我只想研究創(chuàng)建有效性類(lèi)的聲明性樣式。與前面的 DTD 樣本相匹配的一組規(guī)則/類(lèi)包括:
清單 10. gnosis.xml.validity 規(guī)則聲明
    
from gnosis.xml.validity import *
class figure(EMPTY):   pass
class _mixedpara(Or):   _disjoins = (PCDATA, figure)
class paragraph(Some):  _type = _mixedpara
class title(PCDATA):   pass
class _paras(Some):    _type = paragraph
class chapter(Seq):    _order = (title, _paras)
class dissertation(Some): _type = chapter

您可以使用以下命令從這些聲明中創(chuàng)建出實(shí)例:    
ch1 = LiftSeq(chapter, ("1st Title","Validity is important"))
ch2 = LiftSeq(chapter, ("2nd Title","Declaration is fun"))
diss = dissertation([ch1, ch2])
print diss

請(qǐng)注意這些類(lèi)和前面的 DTD 非常匹配。映射基本上是一一對(duì)應(yīng)的;除了有必要對(duì)嵌套標(biāo)記的量化和交替使用中介體之外(中介體名稱(chēng)用前導(dǎo)下劃線(xiàn)標(biāo)出來(lái))。

還要注意的是,這些類(lèi)雖然是用標(biāo)準(zhǔn) Python 語(yǔ)法創(chuàng)建的,但它們也有不同尋常(且更簡(jiǎn)練)之處:它們沒(méi)有方法或?qū)嵗龜?shù)據(jù)。單獨(dú)定義類(lèi),以便從某框架繼承類(lèi),而該框架受到單一的類(lèi)屬性限制。例如, <chapter> 是其它標(biāo)記序列,即 <title> 后面跟著一個(gè)或多個(gè) <paragraph> 標(biāo)記。但是為確保在實(shí)例中遵守約束,我們所需做的就是用這種簡(jiǎn)單的方式來(lái) 聲明chapter 類(lèi)。

編寫(xiě)像 gnosis.xml.validity.Seq 這樣的父類(lèi)程序所涉及的主要“技巧”,就是在初始化期間研究 實(shí)例的 .__class__ 屬性。類(lèi) chapter 自身并不進(jìn)行初始化,因此調(diào)用其父類(lèi)的 __init__() 方法。但是傳遞給父類(lèi) __init__() 的 self 是 chapter 的實(shí)例,而且 self 知道 chapter。為了舉例說(shuō)明這一點(diǎn),下面列出了部分 gnosis.xml.validity.Seq 實(shí)現(xiàn):
清單 11. 類(lèi) gnosis.xml.validity.Seq
    
class Seq(tuple):
  def __init__(self, inittup):
    if not hasattr(self.__class__, '_order'):
      raise NotImplementedError, \
        "Child of Abstract Class Seq must specify order"
    if not isinstance(self._order, tuple):
      raise ValidityError, "Seq must have tuple as order"
    self.validate()
    self._tag = self.__class__.__name__

一旦應(yīng)用程序程序員試圖創(chuàng)建 chapter 實(shí)例,實(shí)例化代碼就檢查是否用所要求的 ._order 類(lèi)屬性聲明了 chapter ,并檢查該屬性是否為所需的元組對(duì)象。方法 .validate() 要做進(jìn)一步的檢查,以確保初始化實(shí)例所用的對(duì)象屬于 ._order 中指定的相應(yīng)類(lèi)。

何時(shí)聲明

聲明性編程樣式在聲明約束方面 幾乎一直比命令式或過(guò)程式樣式更直接。當(dāng)然,并非所有的編程問(wèn)題都是關(guān)于約束的 - 或者說(shuō)至少這并非總是自然定律。但是如果基于規(guī)則的系統(tǒng)(比如語(yǔ)法和推理系統(tǒng))可以進(jìn)行聲明性描述,那么它們的問(wèn)題就比較容易處理了。是否符合語(yǔ)法的命令式驗(yàn)證很快就會(huì)變成非常復(fù)雜難懂的所謂“意大利面條式代碼”(spaghetti code),而且很難調(diào)試。模式和規(guī)則的聲明仍然可以更簡(jiǎn)單。

當(dāng)然,起碼在 Python 中,聲明規(guī)則的驗(yàn)證和增強(qiáng)總是會(huì)歸結(jié)為過(guò)程式檢查。但是把這種過(guò)程式檢查放在進(jìn)行了良好測(cè)試的庫(kù)代碼中比較合適。單獨(dú)的應(yīng)用程序應(yīng)該依靠由像 Spark 或 PLY 或 gnosis.xml.validity 這樣的庫(kù)所提供的更簡(jiǎn)單的聲明性接口。其它像 xmlproc 、 SimpleParse 或 ft.4xslt 這樣的庫(kù),盡管不是 用 Python進(jìn)行聲明的(Python 當(dāng)然適用于它們的領(lǐng)域),也能使用聲明性樣式。


數(shù)據(jù)分析咨詢(xún)請(qǐng)掃描二維碼

若不方便掃碼,搜微信號(hào):CDAshujufenxi

數(shù)據(jù)分析師資訊
更多

OK
客服在線(xiàn)
立即咨詢(xún)
客服在線(xiàn)
立即咨詢(xún)
') } function initGt() { var handler = function (captchaObj) { captchaObj.appendTo('#captcha'); captchaObj.onReady(function () { $("#wait").hide(); }).onSuccess(function(){ $('.getcheckcode').removeClass('dis'); $('.getcheckcode').trigger('click'); }); window.captchaObj = captchaObj; }; $('#captcha').show(); $.ajax({ url: "/login/gtstart?t=" + (new Date()).getTime(), // 加隨機(jī)數(shù)防止緩存 type: "get", dataType: "json", success: function (data) { $('#text').hide(); $('#wait').show(); // 調(diào)用 initGeetest 進(jìn)行初始化 // 參數(shù)1:配置參數(shù) // 參數(shù)2:回調(diào),回調(diào)的第一個(gè)參數(shù)驗(yàn)證碼對(duì)象,之后可以使用它調(diào)用相應(yīng)的接口 initGeetest({ // 以下 4 個(gè)配置參數(shù)為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶(hù)后臺(tái)檢測(cè)極驗(yàn)服務(wù)器是否宕機(jī) new_captcha: data.new_captcha, // 用于宕機(jī)時(shí)表示是新驗(yàn)證碼的宕機(jī) product: "float", // 產(chǎn)品形式,包括:float,popup width: "280px", https: true // 更多配置參數(shù)說(shuō)明請(qǐng)參見(jiàn):http://docs.geetest.com/install/client/web-front/ }, handler); } }); } function codeCutdown() { if(_wait == 0){ //倒計(jì)時(shí)完成 $(".getcheckcode").removeClass('dis').html("重新獲取"); }else{ $(".getcheckcode").addClass('dis').html("重新獲取("+_wait+"s)"); _wait--; setTimeout(function () { codeCutdown(); },1000); } } function inputValidate(ele,telInput) { var oInput = ele; var inputVal = oInput.val(); var oType = ele.attr('data-type'); var oEtag = $('#etag').val(); var oErr = oInput.closest('.form_box').next('.err_txt'); var empTxt = '請(qǐng)輸入'+oInput.attr('placeholder')+'!'; var errTxt = '請(qǐng)輸入正確的'+oInput.attr('placeholder')+'!'; var pattern; if(inputVal==""){ if(!telInput){ errFun(oErr,empTxt); } return false; }else { switch (oType){ case 'login_mobile': pattern = /^1[3456789]\d{9}$/; if(inputVal.length==11) { $.ajax({ url: '/login/checkmobile', type: "post", dataType: "json", data: { mobile: inputVal, etag: oEtag, page_ur: window.location.href, page_referer: document.referrer }, success: function (data) { } }); } break; case 'login_yzm': pattern = /^\d{6}$/; break; } if(oType=='login_mobile'){ } if(!!validateFun(pattern,inputVal)){ errFun(oErr,'') if(telInput){ $('.getcheckcode').removeClass('dis'); } }else { if(!telInput) { errFun(oErr, errTxt); }else { $('.getcheckcode').addClass('dis'); } return false; } } return true; } function errFun(obj,msg) { obj.html(msg); if(msg==''){ $('.login_submit').removeClass('dis'); }else { $('.login_submit').addClass('dis'); } } function validateFun(pat,val) { return pat.test(val); }