<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      SICP:元循環(huán)求值器(Python實(shí)現(xiàn))

      求值器完整實(shí)現(xiàn)代碼我已經(jīng)上傳到了GitHub倉(cāng)庫(kù):TinySCM,感興趣的童鞋可以前往查看。這里順便強(qiáng)烈推薦UC Berkeley的同名課程CS 61A

      在這個(gè)層次結(jié)構(gòu)的最底層是對(duì)象語言。對(duì)象語言只涉及特定的域,而不涉及對(duì)象語言本身(比如它們的文法規(guī)則,或其中的其體句子)。如要涉及它們,則要有一種元語言。對(duì)于語言的兩個(gè)層次這一經(jīng)驗(yàn),所有學(xué)習(xí)外國(guó)語的人都是很熟悉的。然后,就要有一種元元語言來討論元語言,以此類推。

      ——侯世達(dá)《哥德爾、埃舍爾、巴赫:集異璧之大成》

      緒論

      到目前為止,我們探討的都是通過過程抽象、數(shù)據(jù)抽象以及模塊化等手段來控制系統(tǒng)的復(fù)雜性。為了闡釋這些技術(shù),我們一直使用的是同一種編程語言。然而,隨著我們所面對(duì)的問題更復(fù)雜,我們必須經(jīng)常轉(zhuǎn)向新的語言,以便能夠有效地表述自己的想法。實(shí)際上,建立新語言是工程設(shè)計(jì)中控制復(fù)雜性的一種威力強(qiáng)大的工作策略,我們常常能通過采用一種新語言而處理復(fù)雜問題的能力

      元語言抽象就是建立新的語言。它在工程設(shè)計(jì)的所有分支中都扮演著重要的角色,在計(jì)算機(jī)程序設(shè)計(jì)領(lǐng)域更是特別重要。因?yàn)檫@個(gè)領(lǐng)域中,我們不僅可以設(shè)計(jì)新的語言,還可以通過構(gòu)造求值器的方式實(shí)現(xiàn)這些語言。對(duì)某個(gè)程序設(shè)計(jì)語言的求值器(或者解釋器)也是一個(gè)過程,在應(yīng)用于這個(gè)語言的一個(gè)表達(dá)式時(shí),它能夠執(zhí)行求值這個(gè)表達(dá)式所要求的動(dòng)作。

      把這一點(diǎn)看做是程序設(shè)計(jì)語言中最基本的思想一點(diǎn)也不過分:

      求值器決定了一個(gè)程序設(shè)計(jì)語言中各種表達(dá)式的意義,而它本身也不過就是另一個(gè)程序。

      事實(shí)上,我們幾乎可以把任何程序看做是某個(gè)語言的求值器。比如在符號(hào)代數(shù)中,一個(gè)多項(xiàng)式系統(tǒng)包含著多項(xiàng)式的算數(shù)規(guī)則以及底層的實(shí)現(xiàn),一個(gè)符號(hào)求導(dǎo)系統(tǒng)亦包含著函數(shù)求導(dǎo)的規(guī)則以及底層的實(shí)現(xiàn)(參見我之前的博客《SICP:符號(hào)求導(dǎo)、集合表示和Huffman樹(Python實(shí)現(xiàn))》)。從這樣一種觀點(diǎn)看,處理大規(guī)模計(jì)算機(jī)系統(tǒng)的技術(shù),與構(gòu)造新的程序設(shè)計(jì)語言的技術(shù)有緊密的聯(lián)系,而計(jì)算機(jī)科學(xué)中一個(gè)重要的思想就是如何去構(gòu)造適當(dāng)?shù)拿枋稣Z言

      接下來我們將要討論如何關(guān)于在一些語言的基礎(chǔ)上構(gòu)造新的語言。在這篇博客里,我們將用Python語言去構(gòu)造一個(gè)Scheme語言的求值器。事實(shí)上求值器的實(shí)現(xiàn)語言無關(guān)緊要,我們也可以用Scheme語言去構(gòu)造Scheme語言的求值器。用于被求值語言同樣的語言寫出來的求值器被稱為元循環(huán)(metacircular)。

      從根本上說,元循環(huán)求值器也就是我們?cè)诓┛?a href="http://www.rzrgm.cn/orion-orion/p/17185719.html" target="_blank">《SICP:賦值和局部狀態(tài)(Python實(shí)現(xiàn))》中所描述求值的環(huán)境模型的一個(gè)實(shí)現(xiàn)形式。回憶一下,該模型包括兩個(gè)部分:

      • 在求值一個(gè)組合式(combinations)(非特殊形式的復(fù)合表達(dá)式)時(shí),首先求值其中的子表達(dá)式(包括運(yùn)算符和運(yùn)算對(duì)象),然后將運(yùn)算符的值(即一個(gè)過程對(duì)象)應(yīng)用于運(yùn)算對(duì)象的值。
      • 在將一個(gè)復(fù)合過程對(duì)象應(yīng)用于一集實(shí)參時(shí),我們將在一個(gè)新環(huán)境里求值這個(gè)過程的體。構(gòu)造這一新環(huán)境的方式是用一個(gè)幀來擴(kuò)充該過程對(duì)象原來的環(huán)境,幀中包含的是這個(gè)過程的形參與所應(yīng)用的實(shí)參之間的綁定。

      這兩條規(guī)則描述了求值過程的核心部分,也就是它的基本循環(huán)。在這一循環(huán)中,表達(dá)式在環(huán)境中的求值被規(guī)約到過程對(duì)實(shí)參的應(yīng)用,而這種應(yīng)用又被規(guī)約到新的表達(dá)式在新的環(huán)境中的求值。如此下去,直至我們下降到符號(hào)(其值可以在環(huán)境中找到)或基本過程(它們可以直接應(yīng)用)。 如下圖所示:

      這一求值循環(huán)實(shí)際體現(xiàn)為求值器里的兩個(gè)關(guān)鍵函數(shù)scheme_eval()scheme_apply()的相互作用,我們?cè)?.1.1節(jié)將描述他們。

      求值器的實(shí)現(xiàn)依賴于一些定義了被求值表達(dá)式的 語法(syntax) (也即被求值表達(dá)式是以何種數(shù)據(jù)結(jié)構(gòu)來表示的)的過程。我們?nèi)詫⒉捎米皂斚蛳碌臄?shù)據(jù)抽象技術(shù),設(shè)法使求值器獨(dú)立于語言的具體表示。例如,對(duì)于賦值表達(dá)式(set! <var> <val>),我們用抽象的選取函數(shù)assignment_variable()assignment_value()去訪問賦值中相應(yīng)的部分(不過這里需要注意,為了方便后面的類型分派操作,我們?nèi)匀患僭O(shè)可以用first_expr()rest_exprs()分別去訪問表達(dá)式的首元素和其余元素,算是對(duì)抽象性的一個(gè)小小的擊穿吧)。表達(dá)式的具體表示將在4.1.2節(jié)里描述。在4.1.3里還會(huì)描述一些過程和環(huán)境的表示形式,如Environment類、LambdaProcedure類的定義及其對(duì)應(yīng)的一些方法。

      4.1.1 求值器的內(nèi)核

      求值過程可以描述為兩個(gè)函數(shù)scheme_eval()scheme_apply()之間的相互作用(interplay)。

      eval

      scheme_eval()的參數(shù)是一個(gè)表達(dá)式和一個(gè)環(huán)境。scheme_eval()對(duì)表達(dá)式進(jìn)行分類以此展開后續(xù)的求值工作。正如我們上面所說,我們采用選取函數(shù)first_expr()rest_exprs()分別去訪問表達(dá)式的首元素和其余元素,并根據(jù)表達(dá)式的首元素(也即其標(biāo)簽)來完成表達(dá)式類型的判定。表達(dá)式的類型可做如下分類:

      基本表達(dá)式:

      • 對(duì)于自求值表達(dá)式,例如數(shù)和字符串等(其實(shí)也就是我們常說的字面值,literals),scheme_eval()直接返回這個(gè)表達(dá)式本身。
      • 對(duì)于變量,scheme_eval()必須在環(huán)境中查找變量的值。

      特殊形式(special forms):

      • 這里的特殊形式包括if表達(dá)式、變量的賦值/定義、引號(hào)(')表達(dá)式、lambda表達(dá)式等、我們的處理方式是將其分派給不同的名為eval_×××()的求值函數(shù)處理。如if表達(dá)式就分派給eval_if()函數(shù)來處理。

      組合式(combinations):

      • 這里的組合式也即過程的應(yīng)用(application)。對(duì)于一個(gè)過程應(yīng)用,scheme_eval()必須先遞歸地求值組合式的運(yùn)算符部分和運(yùn)算對(duì)象部分。而后將這樣得到的過程對(duì)象和參數(shù)送給scheme_apply(),由它去處理實(shí)際的過程應(yīng)用。注意,如果運(yùn)算符部分是宏(Macro),則不需要事先對(duì)其進(jìn)行求值就直接將其應(yīng)用,待應(yīng)用完畢之后再進(jìn)行求值。

      下面是scheme_eval()的定義:

      @primitive("eval", use_env=True)
      def scheme_eval(expr, env, _=None):
          # Evaluate self-evaluating expressions
          if is_self_evaluating(expr):
              return expr
          # Evaluate variables
          elif is_scheme_variable(expr):
              return env.lookup_variable_value(expr)
      
          # All valid non-atomic expressions are lists (combinations)
          if not is_scheme_pair(expr):
              raise SchemeError(
                  "Unknown expression type: {0}".format(repl_str(expr)))
          first, rest = first_expr(expr), rest_exprs(expr)
          # Evaluate special forms
          if is_scheme_symbol(first) and first in SPECIAL_FORMS:
              return SPECIAL_FORMS[first](rest, env)
          # Evaluate an application
          else:
              operator = scheme_eval(first, env)
              # Check if the operator is a macro
              if isinstance(operator, MacroProcedure):
                  return scheme_eval(complete_apply(operator, rest, env), env)
              operands = rest.map(lambda x: scheme_eval(x, env))
              return scheme_apply(operator, operands, env)
      

      @primitive("eval", use_env=True)表示將其加入Scheme的基本過程中,過程名為eval(注意,Python語言中的裝飾器會(huì)在被裝飾的函數(shù)定義之后立即運(yùn)行,故上述操作是在scheme_eval()函數(shù)定義之后立即完成的)。這里的特殊形式的類型分派采用了數(shù)據(jù)導(dǎo)向的方式(這種做法可以使用戶更容易增加scheme_eval()能分辨的表達(dá)式類型,而又不必修改scheme_eval()的定義本身)。所有的SPECIAL_FORMS及其所分派的求值函數(shù)如下所示:

      SPECIAL_FORMS = {
          # Conditionals
          "if": eval_if,
          "cond": eval_cond,
          "and": eval_and,
          "or": eval_or,
          # Sequencing
          "begin": eval_begin,
          "let": eval_let,
          # Assignments
          "set!": eval_assignment,
          # Definitions
          "define": eval_definition,
          # Lambda expressions
          "lambda": eval_lambda,
          # Quoting
          "quote": eval_quote,
          "unquote": eval_unquote,
          "quasiquote": eval_quasiquote,
          # Dynamic scoping
          "dlambda": eval_dlambda_form,
          # Macro
          "define-macro": eval_macro_definition,
          # Stream
          "delay": eval_delay,
          "cons-stream": eval_cons_stream
      }
      

      注意這里的dlambda特殊形式定義的是采用動(dòng)態(tài)作用域的過程,是原Scheme標(biāo)準(zhǔn)中沒有的。

      apply

      scheme_apply()有兩個(gè)參數(shù),一個(gè)是過程,一個(gè)是該過程去應(yīng)用的實(shí)參表。scheme_apply()將過程分為兩類:基本過程(求值器內(nèi)置)和復(fù)合過程(通過definelambda/dlambda特殊形式來定義)。對(duì)于基本過程,直接調(diào)用該過程對(duì)象的apply()方法去應(yīng)用實(shí)參,對(duì)于復(fù)合過程,則在新建環(huán)境后(該環(huán)境包含了形參綁定到實(shí)參的新幀),順序地求值組成該過程體的那些表達(dá)式。下面是scheme_apply()的定義:

      @primitive("apply", use_env=True)
      def scheme_apply(procedure, arguments, env):
          validate_procedure(procedure)
          if is_primitive_procedure(procedure):
              return procedure.apply(arguments, env)
          elif is_compound_procedure(procedure):
              new_env = procedure.make_call_frame(arguments, env)
              # Note that `tail` is set to True when `eval_sequence()` is called here
              return eval_sequence(procedure.body, new_env, tail=True)
          else:
              raise SchemeError(
                  "Unknown procedure type: {0}".format(repl_str(procedure)))
      

      條件

      eval_if()先在給定的環(huán)境中求值if表達(dá)式的謂詞(predicate)部分,如果得到的結(jié)果為真,eval_if()就去求值這個(gè)if的推論(consequent)部分,否則就求值其替代(alternative)部分。其中需要注意,對(duì)于if語句是肯定有推論部分的,但是我們后面會(huì)將cond語句的求值規(guī)約到對(duì)if語句的求值,因cond語句推論可能為空,故此處需要做出判斷——若推論為空,則返回謂詞的求值結(jié)果,否則照常對(duì)推論求值。此外,如果if語句沒有替代部分,則返回False

      def eval_if(expr, env, tail=True):
          validate_form(expr, min=2, max=3)
      
          if_pred = if_predicate(expr)
          if_predicate_val = scheme_eval(if_pred, env)
          # All values in Scheme are true except `false` object,
          # that is why we need `is_scheme_true()`
          if is_scheme_true(if_predicate_val):
              # Note that for `if` caluse , it muse have consequnet,
              # so `if_consequent` can not be None (although it can be `nil`).
              # But for `cond` clause, `if_consequent` can be None.
              if_conseq = if_consequent(expr)
              if if_conseq is None:
                  return if_predicate_val
              else:
                  return scheme_eval(if_conseq, env, tail=tail)
          # Turn to alternative
          elif len(expr) == 3:
              return scheme_eval(if_alternative(expr), env, tail=tail)
          # If there is no alternative, return False
          else:
              return False
      

      eval_if()對(duì)is_scheme_true()的使用中,我們可以看到被實(shí)現(xiàn)語言(Scheme)和實(shí)現(xiàn)所用的語言(Python)之間的聯(lián)系。if_predicate()在被實(shí)現(xiàn)的語言里求值,產(chǎn)出這一語言里的一個(gè)值,而解釋器的謂詞is_scheme_true()實(shí)際上會(huì)將該值翻譯為可由實(shí)現(xiàn)所用語言的if檢測(cè)的值。這里因?yàn)樵赟cheme中,除了False之外的對(duì)象都應(yīng)被判斷為真,故我們實(shí)際上需要用Python將is_scheme_true()實(shí)現(xiàn)如下:

      def is_scheme_true(val):
          return val is not False
      

      注意這里如果對(duì)象為真則返回該對(duì)象本身,而不是直接返回True
      至于cond表達(dá)式,可以做為派生表達(dá)式(derived expressions)由if表達(dá)式實(shí)現(xiàn)出來,而不必直接去實(shí)現(xiàn)。

      def eval_cond(expr, env, tail=True):
          return scheme_eval(cond_to_if(expr), env, tail)
      

      最后,我們還需要實(shí)現(xiàn)andor這兩個(gè)布爾表達(dá)式:

      def eval_and(exprs, env, tail=True):
          # If there is no expression to be evaluated, return True
          if exprs is nil:
              return True
          # If the last expression is reached (indicating that the values of the
          # previous expressions are all true), then the evaluation result is
          # returned directly
          elif rest_exprs(exprs) is nil:
              return scheme_eval(first_expr(exprs), env, tail=tail)
      
          value = scheme_eval(first_expr(exprs), env)
          # If an expression evaluates to False, return False,
          # and the remaining expressions are not evaluated
          if is_scheme_false(value):
              return False
          else:
              # If an expression evaluates to True, go on,
              return eval_and(rest_exprs(exprs), env)
      
      
      def eval_or(exprs, env, tail=True):
          # If there is no expression to be evaluated, return True
          if exprs is nil:
              return False
          # If the last expression is reached (indicating that the values of the
          # previous expressions are all False), then the evaluation result is
          # returned directly
          elif rest_exprs(exprs) is nil:
              return scheme_eval(first_expr(exprs), env, tail=tail)
      
          value = scheme_eval(first_expr(exprs), env)
          # If an expression evaluates to True, return value, and the remaining
          # expressions are not evaluated
          if is_scheme_true(value):
              return value
          else:
              return eval_or(rest_exprs(exprs), env)
      

      如上所示,andor表達(dá)式都是短路(short-circuited)的。對(duì)于and表達(dá)式,如果其子表達(dá)式有求值為False的,則直接返回False;對(duì)于or表達(dá)式,如果其子表達(dá)式有求值為True的,直接返回True

      序列

      eval_sequence()用在scheme_apply()里,用于求值過程體里的表達(dá)式序列。它也用在scheme_eval()里,用于求值beginlet表達(dá)式內(nèi)的表達(dá)式序列。這個(gè)過程以一個(gè)表達(dá)式序列和一個(gè)環(huán)境為參數(shù),按照序列里表達(dá)式出現(xiàn)的順序?qū)λ鼈冞M(jìn)行求值,并返回最后一個(gè)表達(dá)式的值。

      def eval_sequence(exprs, env, tail=False):
          if not is_scheme_pair(exprs):
              return
          # If `exprs` is the last expression
          if rest_exprs(exprs) is nil:
              # The value of the last expression is returned as the value of the
              # entire `begin` special form(or the body of a procedure)
              return scheme_eval(first_expr(exprs), env, tail)
          else:
              # Evaluate the expressions <expr 1>, <expr 2>, ..., <expr k> in order
              scheme_eval(first_expr(exprs), env)
              return eval_sequence(rest_exprs(exprs), env, tail)
      

      eval_begin()eval_let()亦依據(jù)eval_sequence()來定義:

      def eval_begin(exprs, env, tail=True):
          validate_form(exprs, min=1)
          return eval_sequence(exprs, env, tail)
      
      def eval_let(exprs, env, tail=True):
          validate_form(exprs, min=2)
          let_env = make_let_env(first_expr(exprs), env)
          return eval_sequence(rest_exprs(exprs), let_env, tail=tail)
      

      賦值和定義

      下面過程處理變量賦值,它先調(diào)用scheme_eval()求出需要賦的值,將變量和得到的值傳給env.set_variable_value()方法,將有關(guān)的值安置到環(huán)境env里。

      def eval_assignment(expr, env):
          env.set_variable_value(assignment_variable(
              expr), scheme_eval(assignment_value(expr), env))
      

      變量定義也用類似方式處理:

      def eval_definition(expr, env):
          # Check that expressions is a list of length at least 2
          validate_form(expr, min=2)
      
          var = definition_varaible(expr)
          val = definition_value(expr)
          env.define_variable(var,
                              scheme_eval(val, env))
      
          return var
      

      Lambda表達(dá)式

      對(duì)lambda表達(dá)式進(jìn)行求值時(shí)(注意此時(shí)只是對(duì)lambda表達(dá)式的”定義“進(jìn)行求值),我們會(huì)先分別獲取lambda表達(dá)式的形參和過程體,并結(jié)合lambda表達(dá)式定義時(shí)的環(huán)境來初始化LambdaProcedure對(duì)象(也即默認(rèn)采用的基于詞法作用域規(guī)則)。

      def eval_lambda(expr, env):
          validate_form(expr, min=2)
          parameters = lambda_parameters(expr)
          validate_parameters(parameters)
          body = lambda_body(expr)
          return LambdaProcedure(parameters, body, env)
      

      而對(duì)于我們額外添加的采用動(dòng)態(tài)作用域的dlambda表達(dá)式,在初始化時(shí)則不會(huì)用到其定義時(shí)的環(huán)境。

      def eval_dlambda_form(expr, env):
          validate_form(expr, min=2)
          parameters = expr.first
          validate_parameters(parameters)
          return DLambdaProcedure(parameters, expr.rest)
      

      引號(hào)表達(dá)式

      對(duì)quote表達(dá)式(即')進(jìn)行求值時(shí),直接返回其所引用的表達(dá)式本身即可(無需環(huán)境)。

      def eval_quote(expr, env):
          validate_form(expr, min=1, max=1)
          return text_of_quotation(expr)
      

      對(duì)quasiquote表達(dá)式(即 ` )進(jìn)行求值則是一個(gè)遞歸的過程,在獲得了 ` 所引用的表達(dá)式后,我們需要掃描該表達(dá)式查看是否有被unquote(即以,開頭)的子表達(dá)式,如果遇到了則對(duì)該子表達(dá)式求值,而其余表達(dá)式則保持不求值狀態(tài)。比如(1 2 ,(+ 3 4)) 的求值結(jié)果為'(1 2 7)。注意這里quasiquote內(nèi)部可以嵌套多個(gè)quasiquoteunquote,故需要在遞歸時(shí)維護(hù)depth變量來表示被unquote操作“抵消”后剩余的quasiquote深度。

      def eval_quasiquote(expr, env):
          def quasiquote_item(val, env, depth):
              """Evaluate Scheme expression `val` that is nested at depth `level` in
              a quasiquote form in frame `env`."""
              if not is_scheme_pair(val):
                  return val
      
              # When encountering `unquote`, we decrease the depth by 1.
              # If the depth is 0, we evaluate the rest expressions.
              if is_tagged_list(val, "unquote"):
                  depth -= 1
                  if depth == 0:
                      expr = rest_exprs(val)
                      validate_form(expr, 1, 1)
                      return scheme_eval(first_expr(expr), env)
              elif val.first == "quasiquote":
                  # Leave the item unevaluated
                  depth += 1
      
              # Recursively quasiquote the items of the list
              return val.map(lambda elem: quasiquote_item(elem, env, depth))
      
          validate_form(expr, min=1, max=1)
          # Note that when call `quasiquote_item`, we have encountered
          # the first quasiquote, so depth=1
          return quasiquote_item(text_of_quotation(expr), env, depth=1)
      

      注意unquote操作是不能夠在quasiquote表達(dá)式之外進(jìn)行的,如果在quasiquote之外對(duì)unquote進(jìn)行求值,則會(huì)報(bào)對(duì)應(yīng)的錯(cuò)誤:

      def eval_unquote(expr, env):
          raise SchemeError("Unquote outside of quasiquote")
      

      對(duì)宏的定義,也即define-macro表達(dá)式進(jìn)行求值時(shí),我們選擇將表達(dá)式的形參、表達(dá)式體及環(huán)境都保存到MacroProcedure對(duì)象中,而不立即進(jìn)行求值操作求值。此外,我們還在環(huán)境中添加宏的名稱與MacroProcedure對(duì)象的綁定。

      def eval_macro_definition(expr, env):
          validate_form(expr, min=2)
          target = expr.first
          if is_scheme_pair(target) and is_scheme_symbol(target.first):
              func_name = target.first
              # `target.rest` is parameters,not `target.rest.first`
              parameters = target.rest
              body = expr.rest
              # Just store the expression, rather than evaluate it
              env.define_variable(func_name, MacroProcedure(parameters, body, env))
              return func_name
          else:
              raise SchemeError("Invalid use of macro")
      

      4.1.2 表達(dá)式的表示

      這個(gè)求值器很像我們?cè)诓┛?a href="http://www.rzrgm.cn/orion-orion/p/17026000.html" target="_blank">《SICP:符號(hào)求導(dǎo)、集合表示和Huffman樹(Python實(shí)現(xiàn)) 》中所討論的符號(hào)微分程序。這兩個(gè)程序完成的都是一些對(duì)符號(hào)表達(dá)式的操作。在這兩個(gè)程序中,對(duì)一個(gè)復(fù)合表達(dá)式的求值結(jié)果,也是由對(duì)表達(dá)式片段的遞歸操作來確定的,且對(duì)該結(jié)果的組合方式也是由表達(dá)式的類型來確定。在這兩個(gè)程序中,我們都采用了數(shù)據(jù)抽象技術(shù),將一般性的操作規(guī)則與表達(dá)式的表示方式進(jìn)行了解耦(decouple)。在符號(hào)微分程序中,這意味著同一個(gè)微分程序可以處理前綴(prefix)形式、中綴(infix)形式或其他形式的代數(shù)表達(dá)式。對(duì)于求值器,這意味著被求值語言的語法(syntax)僅僅由對(duì)表達(dá)式進(jìn)行分類和片段提取的過程來決定

      這里需要提一下,我們求值器的輸入是以序?qū)?code>Pair組成的表,也即經(jīng)過詞法分析(lexical analysis)+語法分析(syntactic analysis)后所構(gòu)建的抽象語法樹(abstract syntax tree, AST)。比如對(duì)于 Scheme表達(dá)式

       (+ (* 3 5) (- 10 6))
      

      其對(duì)應(yīng)的表為:

      Pair('+', Pair(Pair('*', Pair(3, Pair(5, nil))), 
                Pair(Pair('-', Pair(10, Pair(6, nil))), nil)))
      

      事實(shí)上由于Scheme程序本身就可視作一棵AST,因此為Scheme程序?qū)懹糜谡Z法分析的Parser異常簡(jiǎn)單。我們可以通過Pair對(duì)象的.first屬性訪問其的第一個(gè)元素,.rest屬性訪問其的后續(xù)元素。

      下面是我們所要實(shí)現(xiàn)的Scheme語言的語法規(guī)范

      • 這里的自求值表達(dá)式包括數(shù)、字符串、nil(也即空表'())、布爾值和undefined(在Python中表示為None):
      def is_self_evaluating(expr):
          return is_scheme_boolean(expr) or is_scheme_number(expr) or \
              is_scheme_null(expr) or is_scheme_string(expr) or expr is None
      
      • 變量用符號(hào)表示:
      def is_scheme_variable(x):
          return is_scheme_symbol(x)
      
      • 引號(hào)表達(dá)式的形式是(quote <text-of-quotation>)(求值器看到的表達(dá)式是以quote開頭的表,即使這種表達(dá)式在輸入時(shí)用的是一個(gè)引號(hào)):
      def text_of_quotation(expr):
          return expr.first
      
      def is_unquote(expr):
          return is_tagged_list(expr, "unquote")
      

      注意這里對(duì)text_of_quotation()函數(shù)而言傳入的expr是不含標(biāo)簽quote的,故直接使用expr.first來訪問<text-of-quotation>即可。

      is_unquote()函數(shù)借助函數(shù)is_tagged_list()來定義,它可用于確定一個(gè)表的開始是不是某個(gè)給定符號(hào):

      def is_tagged_list(expr, tag):
          if is_scheme_pair(expr):
              return expr.first == tag
          else:
              return False
      

      注意我們這里沒有is_quote()函數(shù),這是因?yàn)閷?duì)quote的類型判斷和其它特殊形式一樣,直接在scheme_eval()的分派過程中解決了。

      • 賦值的形式是 (set! <var> <value>)
      def assignment_variable(expr):
          return expr.first
      
      def assignment_value(expr):
          return expr.rest.first
      

      同樣,注意此處的expr不含標(biāo)簽。

      • 定義的形式是:
      (define <var> <value>)
      

      或者

      (define (<var> <parameter 1>... <parameter n>)
          <body>)
      

      后一形式(標(biāo)準(zhǔn)的過程定義)只是下面形式的一種語法包裝:

      (define <var>
          (lambda (<parameter 1> ... <parameter n>)
          <body>))
      

      相應(yīng)的語法函數(shù)是

      def definition_varaible(expr):
          target = expr.first
          # For the case of (define <var> <value>)
          if is_scheme_symbol(target):
              #  `(define x)` or `(define x 2 y 4)` is invalid
              validate_form(expr, min=2, max=2)
              return target
          # For the case of (define (<var> <param 1>, ..., <param n>) <body>)
          elif is_scheme_pair(target) and is_scheme_symbol(target.first):
              return target.first
          else:
              bad_target = target.first if is_scheme_pair(target) else target
              raise SchemeError("Non-symbol: {0}".format(bad_target))
      
      def definition_value(expr):
          target = expr.first
          # For the case of (define <var> <value>)
          if is_scheme_symbol(target):
              return expr.rest.first
          # For the case of (define (<var> <param 1>, ..., <param n>) <body>)
          elif is_scheme_pair(target) and is_scheme_symbol(target.first):
              # Note: The validation of the lambda special form is turned over
              # to `scheme_eval()`
              return make_lambda(target.rest, expr.rest)
          else:
              bad_target = target.first if is_scheme_pair(target) else target
              raise SchemeError("Non-symbol: {0}".format(bad_target))
      

      同樣,注意此處的expr不含標(biāo)簽。

      • lambda表達(dá)式是由符號(hào)lambda開始的表:
      def lambda_parameters(expr):
          return expr.first
      
      def lambda_body(expr):
          return expr.rest
      

      同樣,注意此處的expr不含標(biāo)簽。
      我們還為lambda表達(dá)式提供了一個(gè)構(gòu)造函數(shù),它用在上面的definition_value()里。

      def make_lambda(parameters, body):
          return scheme_cons("lambda", scheme_cons(parameters, body))
      
      • 條件表達(dá)式由if開始,有一個(gè)謂詞部分,一個(gè)推論部分和一個(gè)(可缺的)替代部分。如果這一表達(dá)式?jīng)]有替代部分,我們就用False做為其替代。
      def if_predicate(expr):
          return expr.first
      
      def if_consequent(expr):
          return expr.rest.first
      
      def if_alternative(expr):
          return expr.rest.rest.first
      

      我們也為if表達(dá)式提供了一個(gè)構(gòu)造函數(shù),它在cond_to_if中使用,用于將cond表達(dá)式變換為if表達(dá)式。

      def make_if(predicate, consequent, alternative):
          return scheme_list("if", predicate, consequent, alternative)
      
      • begin包裝起一個(gè)表達(dá)式序列。這里我們?cè)O(shè)計(jì)選擇函數(shù)返回序列中的第一個(gè)表達(dá)式和其余表達(dá)式。
      def first_expr(seq):
          return seq.first
      
      def rest_exprs(seq):
          return seq.rest
      

      我們還設(shè)計(jì)了一個(gè)構(gòu)造函數(shù)sequence_to_expr()(用在cond_to_if里),它把一個(gè)序列變換為一個(gè)表達(dá)式,如果需要的話就加上begin作為開頭:

      def sequence_to_expr(seq):
          if seq is nil:
              return seq
          elif rest_exprs(seq) is nil:
              return first_expr(seq)
          else:
              return make_begin(seq)
      
      def make_begin(seq):
          return scheme_cons("begin", seq)
      
      • let表達(dá)式在規(guī)約到用eval_sequence()對(duì)表達(dá)式序列進(jìn)行求值之前,需要新建環(huán)境。該新環(huán)境的構(gòu)造函數(shù)下:
      def make_let_env(bindings, env):
          def bindings_items(bindings, env):
              if bindings is nil:
                  return nil, nil
              binding = bindings.first
              validate_form(binding, min=2, max=2)
              var = binding.first
              val = scheme_eval(binding.rest.first, env)
              vars, vals = bindings_items(bindings.rest, env)
              return scheme_cons(var, vars), scheme_cons(val, vals)
      
          if not is_scheme_list(bindings):
              raise SchemeError("Bad bindings list in let form")
      
          vars, vals = bindings_items(bindings, env)
          validate_parameters(vars)
          return env.extend_environment(vars, vals)
      

      派生表達(dá)式
      在一些語言中,一些特殊形式可以基于其他特殊形式的表達(dá)式定義出來,而不必直接去實(shí)現(xiàn),比如cond表達(dá)式和let表達(dá)式。這樣采用語法變換的方式實(shí)現(xiàn)的表達(dá)式稱為派生表達(dá)式(derived expressions)

      • cond表達(dá)式可以實(shí)現(xiàn)為一些嵌套的if表達(dá)式。例如,我們可以將對(duì)于下列表達(dá)式的求值問題:
      (cond ((> x 0) x)
            ((= x 0) (display 'zero) 0)
            (else (- x)))
      

      規(guī)約為對(duì)下面涉及ifbegin的表達(dá)式的求值問題:

      (if (> x 0)
          x
          (if (= x 0)
              (begin (display 'zero)
                      0)
              (- x)))
      

      采用這種方式實(shí)現(xiàn)對(duì)cond的求值能簡(jiǎn)化求值器。
      下面展示了提取cond表達(dá)式中各個(gè)部分的語法過程,以及函數(shù)cond_to_if(),它能將cond表達(dá)式變換為if表達(dá)式。一個(gè)分情況分析以cond開始,并包含一個(gè)謂詞-動(dòng)作子句的表。如果一個(gè)子句的符號(hào)是else,那么就是一個(gè)else子句。

      def cond_predicate(clause):
          return clause.first
      
      def cond_actions(clause):
          return clause.rest
      
      def cond_to_if(exprs):
          return expand_clauses(exprs)
      
      def expand_clauses(clauses):
          # return None means that interpreter does not print anything
          if clauses is nil:
              return None
          first = clauses.first
          rest = clauses.rest
          validate_form(first, min=1)
          if cond_predicate(first) == "else":
              if rest is nil:
                  return sequence_to_expr(first.rest)
              else:
                  raise SchemeError(
                      "ELSE clause is not last: {0}".format(
                          repl_str(clauses)))
          else:
              if cond_actions(first) is nil:  # for example, (cond ((= 1 1)))
                  # there is no consequent, we denote it as None
                  # o distinguish it from nil
                  if_consequent = None
              else:  # for example, (cond ((= 1 1) 2)) or (cond ((= 1 1) nil))
                  # there is a consequent, including nil
                  if_consequent = sequence_to_expr(first.rest)
              return make_if(cond_predicate(first), if_consequent, cond_to_if(
                  rest))
      

      4.1.3 求值器數(shù)據(jù)結(jié)構(gòu)

      除了需要定義表達(dá)式的外部語法之外,求值器的實(shí)現(xiàn)還必須定義好在其內(nèi)部實(shí)際操作的數(shù)據(jù)結(jié)構(gòu),做為程序執(zhí)行的一部分。例如序列數(shù)據(jù)結(jié)構(gòu),過程和環(huán)境的表示形式,真和假的表示方式等等。

      序列數(shù)據(jù)結(jié)構(gòu)
      如我們?cè)诓┛?a href="http://www.rzrgm.cn/orion-orion/p/16234680.html" target="_blank">《SICP: 層次性數(shù)據(jù)和閉包性質(zhì)(Python實(shí)現(xiàn))》中所言,序列數(shù)據(jù)結(jié)構(gòu)由序?qū)磉M(jìn)行層次化定義,序?qū)Φ亩x如下:

      class Pair:
          def __init__(self, first, rest):
              self.first = first
              self.rest = rest
      
          def __len__(self):
              n, rest = 1, self.rest
              while isinstance(rest, Pair):
                  n += 1
                  rest = rest.rest
              # The tail of the list must be nil
              if rest is not nil:
                  raise TypeError("length attempted on improper list")
              return n
      
          def __eq__(self, p):
              if not isinstance(p, Pair):
                  return False
              return self.first == p.first and self.rest == p.rest
      
          def map(self, fn):
              mapped = fn(self.first)
              if self.rest is nil or isinstance(self.rest, Pair):
                  return Pair(mapped, self.rest.map(fn))
              else:
                  raise TypeError("ill-formed list (cdr is a promise)")
      
          def flatmap(self, fn):
              from primitive_procs import scheme_append
              mapped = fn(self.first)
              if self.rest is nil or isinstance(self.rest, Pair):
                  return scheme_append(mapped, self.rest.flatmap(fn))
              else:
                  raise TypeError("ill-formed list (cdr is a promise)")
      

      此外,我們還需要一個(gè)空表類及其實(shí)例:

      class nil:
          def __len__(self):
              return 0
      
          def map(self, fn):
              return self
      
          def flatmap(self, fn):
              return self
      
      nil = nil()
      

      注意,nil實(shí)例將與其相同的類名進(jìn)行了覆蓋,且該空表類自始至終只有一個(gè)實(shí)例,所以我們可以直接使用is nil語法來測(cè)試某個(gè)對(duì)象是否為空表。

      謂詞檢測(cè)
      為了實(shí)現(xiàn)條件表達(dá)式,我們把除了False對(duì)象之外的所有東西都接受為真:

      def is_scheme_true(val):
          return val is not False
      
      
      def is_scheme_false(val):
          return val is False
      

      除了謂詞檢測(cè)之外,還有許多諸如is_scheme_pair()is_scheme_list()之類的Scheme內(nèi)置數(shù)據(jù)結(jié)構(gòu)檢測(cè)函數(shù),就不在此處進(jìn)行一一列舉了,大家可直接參考我項(xiàng)目目錄下的primitive_procs.py文件。

      過程的表示
      過程分為基本過程(primitive procedures)和復(fù)合過程(compound procedures,也即由define語句或lambda/dlambda語句定義的過程)。我們先定義基本過程如下:

      class Procedure:
      
      class PrimitiveProcedure(Procedure):
          import primitive_procs as pprocs
      
          def __init__(self, fn, name="primitive", use_env=False):
              self.name = name
              self.fn = fn
              self.use_env = use_env
      
          def apply(self, arguments, env):
              if not self.pprocs.is_scheme_list(arguments):
                  raise self.pprocs.SchemeError(
                      "Arguments are not in a list: {0}".format(arguments))
      
              # Convert a Scheme list to a Python list
              arguments_list = self.flatten(arguments)
              try:
                  if self.use_env:
                      return self.fn(*arguments_list, env)
                  return self.fn(*arguments_list)
              except TypeError:
                  raise self.pprocs.SchemeError(
                      "Incorrect number of arguments: {0}".format(self))
      
          def flatten(self, arguments):
              if arguments is nil:
                  return []
              else:
                  return [arguments.first] + self.flatten(arguments.rest)
      

      下列函數(shù)可檢查過程是否為基本過程:

      def is_primitive_procedure(procedure):
          return isinstance(procedure, PrimitiveProcedure)
      

      復(fù)合過程則包括形參parameters、過程體body和環(huán)境body

      class LambdaProcedure(Procedure):
          import primitive_procs as pprocs
      
          def __init__(self, parameters, body, env):
              assert isinstance(env, Environment), "env must be of type Environment"
              self.pprocs.validate_type(parameters, self.pprocs.is_scheme_list,
                                        0, "LambdaProcedure")
              self.pprocs.validate_type(
                  body, self.pprocs.is_scheme_list, 1, "LambdaProcedure")
              self.parameters = parameters
              self.body = body
              self.env = env
      
          def make_call_frame(self, arguments, env):
              return self.env.extend_environment(self.parameters, arguments)
      

      復(fù)合過程的make_call_frame()方法用于將復(fù)合過程對(duì)象應(yīng)用于實(shí)參時(shí),向過程定義時(shí)的環(huán)境self.env中添加一個(gè)新幀(注意不是調(diào)用時(shí)環(huán)境env,調(diào)用時(shí)環(huán)境env并未使用),從而擴(kuò)展得到一個(gè)新的環(huán)境。具體的env.extend_environment()方法我們會(huì)在環(huán)境的表示部分再講述如何實(shí)現(xiàn)。

      下列函數(shù)可檢查過程是否為復(fù)合過程:

      def is_compound_procedure(procedure):
          return isinstance(procedure, Procedure)
      

      使用動(dòng)態(tài)作用域的dlambda過程對(duì)象定義如下:

      class DLambdaProcedure(Procedure):
          def __init__(self, parameters, body):
              self.parameters = parameters
              self.body = body
      
          def make_call_frame(self, arguments, env):
              return env.extend_environment(self.parameters, arguments)
      

      該過程也是復(fù)合過程,唯一的區(qū)別是在過程應(yīng)用時(shí),建立的新環(huán)境是基于調(diào)用時(shí)環(huán)境env來擴(kuò)展的,而不是定義時(shí)環(huán)境。

      環(huán)境的表示
      求值器還需要定義對(duì)環(huán)境的表示。正如我們?cè)诓┛?a href="http://www.rzrgm.cn/orion-orion/p/17247251.html" target="_blank">《SICP:求值和環(huán)境模型(Python實(shí)現(xiàn))》中所說,一個(gè)環(huán)境就是一個(gè)幀的序列,每個(gè)幀都是一個(gè)包含綁定的表格,其中綁定關(guān)聯(lián)起一些變量和與之對(duì)應(yīng)的值。

      我們首先定義好幀:

      class Frame:
          def __init__(self):
              self.bindings = {}
      
          def add_binding(self, var, val):
              self.bindings[var] = val
      
          def set_var(self, var, val):
              self.add_binding(var, val)
      

      然后在幀的基礎(chǔ)之上定義好環(huán)境:

      class Environment:
          import primitive_procs as pprocs
      
          def __init__(self):
              self.frames = self.pprocs.scheme_list(Frame())
      
          def lookup_variable_value(self, var):
              ...
          def extend_environment(self, vars, vals):
              ...
          def define_variable(self, var, val):
              ...
          def set_variable_value(self, var, val):
              ...
      

      和環(huán)境有關(guān)的方法lookup_variable_value()extend_environment()define_variable()set_variable_value()我們?cè)谙旅孢M(jìn)行分別闡述。

      • lookup_variable_value()方法返回符號(hào)var在環(huán)境里的綁定值,如果這一變量沒有綁定就發(fā)出一個(gè)錯(cuò)誤信號(hào):
      def lookup_variable_value(self, var):
          def env_loop(frames):
              # If cannot find the variable in the current environment
              if self.pprocs.is_scheme_null(frames):
                  raise self.pprocs.SchemeError(
                      "Unbound variable: {0}".format(var))
              frame = frames.first
              if var in frame.bindings.keys():
                  return frame.bindings[var]
              else:
                  return env_loop(frames.rest)
          return env_loop(self.frames)
      
      • extend_environment()方法返回一個(gè)新環(huán)境,這個(gè)環(huán)境里包含了一個(gè)新幀,其中所有位于表vars里的符號(hào)綁定到表vals里對(duì)應(yīng)的元素,而該幀的外圍環(huán)境是當(dāng)前這個(gè)對(duì)象所對(duì)應(yīng)的環(huán)境。
      def extend_environment(self, vars, vals):
          new_env = Environment()
          if len(vars) == len(vals):
              new_env.frames = self.pprocs.scheme_cons(
                  self.make_frame(vars, vals),
                  self.frames)
          elif len(vars) < len(vals):
              raise self.pprocs.SchemeError(
                  "Too many arguemtns supplied")
          else:
              raise self.pprocs.SchemeError(
                  "Too few arguemtns supplied")
          return new_env
      
      @ staticmethod
      def make_frame(vars, vals):
          frame = Frame()
          while isinstance(vars, Pair):
              var = vars.first
              val = vals.first
              frame.add_binding(var, val)
              vars = vars.rest
              vals = vals.rest
          return frame
      
      • define_variable()方法在當(dāng)前對(duì)象所對(duì)應(yīng)環(huán)境的第一個(gè)幀里加入一個(gè)新綁定,它關(guān)聯(lián)起變量varval
      def define_variable(self, var, val):
              frame = self.frames.first
              frame.add_binding(var, val)
      
      • set_variable_value()方法修改變量var在當(dāng)前對(duì)象所對(duì)應(yīng)環(huán)境里的綁定,使得該變量現(xiàn)在綁定到值val。如果這一變量沒有綁定就發(fā)出一個(gè)錯(cuò)誤信號(hào)。
      def set_variable_value(self, var, val):
          def env_loop(frames):
              # 空表
              if self.pprocs.is_scheme_null(frames):
                  raise self.pprocs.SchemeError(
                      "Unbound variable: {0}".format(var))
              frame = frames.first
              if var in frame.bindings.keys():
                  frame.set_var(var, val)
                  return
              else:
                  env_loop(frames.rest)
      
          env_loop(self.frames)
      

      不過需要注意的是,這里所描述的方法,只不過是表示環(huán)境的許多方法之一。由于前面采用了數(shù)據(jù)抽象的技術(shù),而這已經(jīng)將求值器的其它部分與這些表示細(xì)節(jié)隔離開,因此如果需要的話我們也完全可以修改環(huán)境的表示。在產(chǎn)品質(zhì)量的Lisp系統(tǒng)里,求值器中環(huán)境操作的速度——特別是查找變量的速度——對(duì)系統(tǒng)的性能有著重要的影響。這里所描述的表示方式雖然在概念上非常簡(jiǎn)單,但其工作效率卻很低,通常不會(huì)用在產(chǎn)品系統(tǒng)里。其低效的原因來自求值器為了找到一個(gè)給定變量的綁定,需要搜索許多個(gè)幀。這樣一種方式稱為深約束。避免這一低效性的方法是采用一種稱為語法作用域的策略,可參考原書5.5.6節(jié)。

      4.1.4 作為程序運(yùn)行這個(gè)求值器

      有了一個(gè)求值器,我們手頭上就有了一個(gè)有關(guān)Lisp表達(dá)式如何求值的程序描述(這是用Python描述的)。接下來我們來看如何運(yùn)行這個(gè)程序。

      我們的求值器程序最終把表達(dá)式規(guī)約到基本過程的應(yīng)用(基本過程的定義在項(xiàng)目代碼中的primitive_procs.py文件中)。而每個(gè)基本過程名都必須有一個(gè)綁定,以便當(dāng)scheme_eval()求值一個(gè)應(yīng)用基本過程的運(yùn)算符時(shí),可以找到相應(yīng)的對(duì)象,并調(diào)用這個(gè)對(duì)象的apply方法。為此我們必須創(chuàng)建起一個(gè)初始環(huán)境,在其中建立起基本過程的名字與一個(gè)唯一對(duì)象的關(guān)聯(lián),在求值表達(dá)式時(shí)的過程中可能遇到這些名字。

      def setup_environment():
          initial_env = Environment()
          initial_env.define_variable("undefined", None)
          add_primitives(initial_env, PRIMITIVE_PROCS)
          return initial_env
      

      這里的PRIMITIVE_PROCS是一個(gè)Python全局變量,具體而言是一個(gè)存儲(chǔ)了基本過程函數(shù)及其名稱的表。基本過程對(duì)象的具體表示方式我們已經(jīng)在上文中提到,也就是PrimitiveProcedure,因此我們可以用下列函數(shù)將基本過程名稱及其對(duì)象的綁定添加到全局環(huán)境中:

      def add_primitives(env, funcs_and_names):
          for fn, name, use_env in funcs_and_names:
              env.define_variable(name, PrimitiveProcedure(
                  fn, name=name, use_env=use_env))
      

      為了能夠很方便地運(yùn)行這個(gè)元循環(huán)求值器,我們使用函數(shù)read_eval_print_loop()來模擬Lisp中的讀入-求值-打印循環(huán)。這個(gè)循環(huán)打印出一個(gè)提示符(prompt),讀入輸入表達(dá)式,進(jìn)行詞法分析和語法分析轉(zhuǎn)換為AST后,再在全局環(huán)境里求值這個(gè)表達(dá)式,而后打印出得到的結(jié)果。

      def read_eval_print_loop(env, infile_lines=None, interactive=False,
                               quiet=False, startup=False, load_files=(),
                               report_errors=False, print_ast=False):
          if startup:
              for filename in load_files:
                  scheme_load(filename, True, env)
          # Initialize a tokenizer instance
          tokenizer = Tokenizer()
          # Initialize a parser instance
          parser = Parser()
          while True:
              try:
                  lines_stream = read_input(infile_lines, input_prompt="scm> ")
      
                  # Tokenize the input lines
                  lines_stream = (tokenizer.tokenize(line) for line in lines_stream)
      
                  # Parse a single expression / multiple expressions util all the
                  # tokens are consumed
                  while True:
                      # Parse a complete expression (single-line or multi-line) at a
                      # time
                      ast = parser.parse(lines_stream)
                      result = scheme_eval(ast, env)
                      if not quiet:
                          print(repl_str(result))
                      # If all the tokens read are consumed, then break
                      if parser.is_empty():
                          break
              except (SchemeError, SyntaxError, ValueError, RuntimeError) as err:
                  ...
      
      
      def read_input(infile_lines, input_prompt):
          if infile_lines:  # If use a file stream as input
              while infile_lines:
                  line = infile_lines.pop(0).strip("\n")
                  yield line
              raise EOFError
          else:  # if use a keyboard stream as input
              while True:
                  yield input(input_prompt)
                  # If a multi-line expression input is not
                  # terminated, use whitespace as the
                  # the input prompt to read more lines.
                  input_prompt = " " * len(input_prompt)
      

      為了運(yùn)行這個(gè)求值器,現(xiàn)在我們需要著的全部事情就是初始化這個(gè)全局環(huán)境,并啟動(dòng)上述的讀入-求值-打印循環(huán)。

      def main():
          args = parse_args()
      
          sys.path.insert(0, "")
      
          interactive = True
          load_files = []
      
          if args.load:
              for filename in args.filenames:
                  load_files.append(filename)
      
          the_global_env = setup_environment()
          read_eval_print_loop(env=the_global_env, startup=True,
                               interactive=interactive, load_files=load_files,
                               print_ast=args.ast)
      

      下面是一個(gè)交互過程實(shí)例:

      scm>  (define (make-withdraw balance)
                  (lambda (amount)
                      (If (>= balance amount)
                              (begin (set! balance (- balance amount))
                                     balance)
                              "Insufficient funds")))
      make-withdraw
      scm> (define W1 (make-withdraw 100))
      w1
      scm> (define W2 (make-withdraw 100))
      w2
      scm> (W1 50)
      50
      scm> (W2 70)
      30
      scm> (W2 40)
      "Insufficient funds"
      scm> (W1 40)
      10
      

      4.1.5 將數(shù)據(jù)作為程序

      在思考求值Lisp表達(dá)式的Python程序時(shí),有一個(gè)類比可能很有幫助。關(guān)于程序意義的一種操作式觀點(diǎn)(operational view),就是將程序看做是一種抽象的(可能無窮大的)機(jī)器的一個(gè)描述。比如考慮下面的Lisp求階乘程序:

      (define (factorial n)
          (if (= n 1)
              1
              (* (factorial (- n 1)) n)))
      

      我們可以將這一程序看成一部機(jī)器的描述。這部機(jī)器包含的部分有遞減(decrement)、乘法(multiply)以及相等測(cè)試(tests for equality),還有一個(gè)兩位開關(guān)(即if分支)和另一部階乘機(jī)器(這樣,階乘機(jī)器就是無窮的,因?yàn)槠渲邪硪徊侩A乘機(jī)器)。下圖是這部階乘機(jī)器的流程圖,說明了有關(guān)的部分如何連接在一起。

      按照類似的方式,我們也可以把求值器看做是一部非常特殊的機(jī)器,它要求以一部機(jī)器的描述作為輸入。給定了這個(gè)輸入之后,求值器就能夠規(guī)劃自己的行為(configures itself)來模擬被描述的機(jī)器。舉例來說,如果我們將factorial過程的定義送入求值器,求值器能夠計(jì)算階乘,如下圖所示:

      按照這一觀點(diǎn),我們的求值器可以被看做是是一種通用機(jī)器(universal machine)。它能夠模擬其它的任何機(jī)器,只要它們已經(jīng)被描述為L(zhǎng)isp程序。這是非常驚人的。比如我們?yōu)殡娮与娐吩O(shè)想一種類似的求值器,這將會(huì)是一種電路,它以另一個(gè)電路(例如某個(gè)濾波器)的信號(hào)編碼方案做為輸入。在給了它這種輸入之后,這一電路求值器能具有與這一描述所對(duì)應(yīng)的濾波器同樣的行為。這樣的一個(gè)通用電子線路將會(huì)難以想象的復(fù)雜,而程序的求值器卻是一個(gè)相當(dāng)簡(jiǎn)單的程序。

      事實(shí)上,任一求值器都能模擬其他的求值器。這樣,有關(guān)“原則上說什么可以計(jì)算”(忽略掉有關(guān)所需時(shí)間和空間的實(shí)踐性問題)的概念就與語言或計(jì)算機(jī)無關(guān)了,它反映的是一個(gè)有關(guān)可計(jì)算性(computability) 的基本概念。這一思想第一次是由圖靈以清晰的方式闡述,他在1936年的論文《論可計(jì)算數(shù)及其在判定性問題上的應(yīng)用》[2]為理論計(jì)算機(jī)科學(xué)奠定了基礎(chǔ)。在這篇論文里,圖靈給出了一種簡(jiǎn)單的計(jì)算模型——現(xiàn)在被稱為圖靈機(jī)——并聲稱,任何“有效過程”都可以描述為這種機(jī)器的一個(gè)程序(這一論斷就是著名的邱奇—圖靈論題)。圖靈而后實(shí)現(xiàn)了一臺(tái)通用機(jī)器,即一臺(tái)圖靈機(jī),其行為就像是所有圖靈機(jī)程序的求值器。

      求值器的另一驚人方面,在于它就像是在我們的程序設(shè)計(jì)語言所操作的數(shù)據(jù)對(duì)象和這個(gè)程序設(shè)計(jì)語言本身之間的一座橋梁。現(xiàn)在設(shè)想這個(gè)用Python實(shí)現(xiàn)的求值程序正在運(yùn)行,一個(gè)用戶正在輸入表達(dá)式并觀察所得到的結(jié)果。從用戶的觀點(diǎn)看,他所輸入的形如(* x x)的表達(dá)式是程序設(shè)計(jì)語言里的一個(gè)表達(dá)式,是求值器將要執(zhí)行的東西。而從求值器的觀點(diǎn)看,這一表達(dá)式不過是一個(gè)表(這里就是三個(gè)符號(hào)*xx的表),它所要做的也就是按照一套定義良好的規(guī)則去操作這個(gè)表。

      這種用戶程序即求值器的數(shù)據(jù)的情況,未必會(huì)成為混亂之源。事實(shí)上,有時(shí)簡(jiǎn)單地忽略這種差異,為用戶提供顯式地將數(shù)據(jù)對(duì)象當(dāng)做Lisp表達(dá)式求值的能力,允許他們?cè)诔绦蚶镏苯邮褂?code>eval,甚至可能帶來許多方便。在我們實(shí)現(xiàn)的這個(gè)求值器里,就可以直接使用eval過程:

      scm> (eval '(* 5 5))
      25
      scm> (eval (cons '* (list 5 5)))
      25
      

      事實(shí)上,Python語言中也內(nèi)置了eval()函數(shù):

      >>> eval("5 * 5")
      25
      

      參考

      • [1] Abelson H, Sussman G J. Structure and interpretation of computer programs[M]. The MIT Press, 1996.
      • [2] Turing A M. On computable numbers, with an application to the Entscheidungsproblem[J]. J. of Math, 1936, 58(345-363): 5.
      posted @ 2023-05-15 20:14  orion-orion  閱讀(682)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 无码任你躁久久久久久久| 高清美女视频一区二区三区| 巨胸不知火舞露双奶头无遮挡| 国产成人午夜福利在线播放| 亚洲性图日本一区二区三区| 高清一区二区三区不卡视频| 精品国产亚洲一区二区三区在线观看| 最新国产精品好看的精品| 一区二区三区成人| 国产最新进精品视频| 华亭县| 日韩中文字幕v亚洲中文字幕 | 久久精品国产99麻豆蜜月| AV毛片无码中文字幕不卡| 麻豆一区二区中文字幕| 国产无套内射又大又猛又粗又爽 | 久久综合给合久久狠狠狠88 | 国产精品无码久久久久| 人人做人人澡人人人爽| 国产成人精品一区二区不卡| yw尤物av无码国产在线观看| 国产综合久久久久久鬼色| 国产拗精品一区二区三区| 做暖暖视频在线看片免费| 99亚洲男女激情在线观看| 人妻少妇精品系列一区二区| 午夜福利片1000无码免费| 国产精品无码v在线观看| 亚洲人成网站在线无码| 日韩精品人妻av一区二区三区| 免费看美女被靠到爽的视频| 久久99精品网久久| 欧美成人午夜在线观看视频| 国产精品熟妇视频国产偷人| 久久一亚色院精品全部免费| 岛国岛国免费v片在线观看| 国内精品久久久久久久97牛牛| 亚洲欧美综合人成在线| 国产成人亚洲综合图区| 少妇被粗大的猛进69视频| 成人综合婷婷国产精品久久蜜臀|