讀取-求值-輸出循環

「讀取-求值-輸出」循環(英語:Read-Eval-Print Loop,簡稱REPL),也被稱做交互式頂層構件(英語:interactive toplevel),是一個簡單的,交互式的編程環境。這個詞常常用於指代一個Lisp的交互式開發環境,也能指代命令行的模式。

概述

編輯

「讀入-求值-輸出」循環 的名字來自於以下幾個Lisp用來實現這種機制的內置函數:

  • 讀入函數接收一個來自於用戶的表達式,將其解析成數據結構並存入記憶體。例如,用戶可能會輸入一個s-表達式 (+ 1 2 3),這句話會被解析成一個包含四個元素的鍊表。
  • 求值函數 負責處理內部的數據結構並對其求值。在Lisp中,求一個以函數名開頭的s-表達式意味着對接下來的參數調用那個函數。所以函數"+"被在參數1 2 3上調用,產生結果6
  • 輸出函數接受求值結果,並將其呈現給用戶。儘管當前的結果「6」並不具有複雜的格式,但如果是一個較為複雜的表達式,那麼它將會被精心處理,以便於更方便地被理解。

REPL使得探索性的編程和調試更加便捷,因為「讀取-求值-輸出」循環通常會比經典的「編輯-編譯-運行-調試」模式要更快。

優點

編輯

REPL對於學習一門新的程式語言具有很大的幫助,因為它能立刻對初學者做出回應。許多工具集和程式語言使用REPL研究算法、進行調試,比如MATLABROOT頁面存檔備份,存於互聯網檔案館),SciPyIPython。Python的doctest模塊能夠通過捕捉自身REPL命令行的輸出使測試代碼更容易地進行。

由於 print 函數輸出的數據格式(字符串)與用戶輸入的數據格式(字符串)相同,大多數輸出的結果也可以被帶回到 read 函數作為輸入。然而,有的時候輸出的結果只能代表求值結果而不是求值結果本身,比如一個socket句柄或一些類的實例。比如在Python中使用 <__模块名__.类名 实例名> 這種形式來代表一個實例本身,在Common Lisp當中就使用 #<whatever> 的形式。而在CLIM,SLIME以及Symbolics Lisp Machine的REPL卻有辦法讀取很難被完全字符串化的這些對象。他們記錄被輸出過的對象,之後當代碼被讀取時,這些對象能夠被解析並重新被使用。

實現

編輯

為了實現一個 Lisp REPL,只需要實現這三個函數和一個不停輪詢的函數即可(當然,求值函數的實現是最為複雜的,因為它在內部要實現像 car+ 的原始函數以及像if 一樣的特殊操作符)。這些工作完成了之後,一個基本的REPL就可以用如下的簡單形式表達:(loop (print (eval (read))))

一種實現eval的方式就是實現一個遞歸處理抽象語法樹(該語法樹被 read 函數創建)的函數。另一種可行的方法是將這個抽象語法樹編譯為機器碼並執行。

主要的REPL程式語言環境

編輯

APLBASICClojureF#HaskellJJuliaPerlPHPPrologPythonRRubyScalaSmalltalkStandard MLSwiftTclJavascriptJava(自JDK 9起)

外部連結

編輯