Write Yourself a Scheme in 48 Hours/Building a REPL

原文晾蜘。
https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Building_a_REPL

目前為止伐蒂,我們已經(jīng)能夠計算從命令行中讀取的單句表達式并將結(jié)果打印出來了。這對一個計算器來說已經(jīng)夠了秃臣,但離大部分心中所想的“編程”還有一些距離沸枯。我們希望能夠定義函數(shù)和變量并在之后進行使用肤视。但在這之前徙赢,我們需要構(gòu)建一個能夠執(zhí)行多個語句而不退出程序的系統(tǒng)字柠。

與之前一次性執(zhí)行整個程序不同,我們這次需要構(gòu)建一個REPL(讀取->求值->打印的循環(huán))狡赐。它會以交互的方式每次都會從控制臺讀入一個表達式并將執(zhí)行得到的結(jié)果打印在表達式之后窑业。后面的表達式能夠引用之前表達式中設(shè)置的變量(在下一章之后我們就能夠這么做了),讓你能夠這樣來構(gòu)建你自己的函數(shù)庫枕屉。

首先我們需要導(dǎo)入一些額外的IO函數(shù)数冬。在程序開頭添加以下內(nèi)容:

import System.IO

接下來,我們定義一些輔助函數(shù)來簡化我們的IO任務(wù)搀庶。我們需要一個打印出字符串然后立刻將輸出流清空的函數(shù);不然的話铜异,輸出的內(nèi)容依然會保存在緩沖區(qū)中這樣用戶就不會看到提示符或是結(jié)果:

flushStr :: String -> IO ()
flushStr str = putStr str >> hFlush stdout

接下來哥倔,我們創(chuàng)建一個打印出提示符然后讀入一行輸入的函數(shù):

readPrompt :: String -> IO String
readPrompt prompt = flushStr prompt >> getLine

現(xiàn)在我們讓代碼對字符串進行解析和求值,將主函數(shù)中的錯誤捕獲起來并在函數(shù)內(nèi)部進行處理:

evalString :: String -> IO String
evalString expr = return $ extractValue $ trapError (liftM show $ readExpr expr >>= eval)

然后寫一個對字符串進行計算然后打印出結(jié)果的函數(shù):

evalAndPrint :: String -> IO ()
evalAndPrint expr =  evalString expr >>= putStrLn

現(xiàn)在是時候把它們都串聯(lián)起來了揍庄。我們想要在一個無限的循環(huán)中每次都讀取輸入咆蒿,調(diào)用函數(shù),然后將結(jié)果打印出來蚂子。內(nèi)置的interact函數(shù)基本上能做到除了循環(huán)以外的所有事情沃测。但如果使用一個sequence . repeat . interact的組合,我們就會得到一個無限的循環(huán)食茎,但卻沒辦法從中退出去了蒂破。所以我們需要在其中啟動我們自己的循環(huán):

until_ :: Monad m => (a -> Bool) -> m a -> (a -> m ()) -> m ()
until_ pred prompt action = do 
   result <- prompt
   if pred result 
      then return ()
      else action result >> until_ pred prompt action

后面帶有下劃線的名字是Haskell里的一個典型的命名規(guī)范,表達不斷重復(fù)卻沒有返回值的Monad函數(shù)别渔。until_讀取一個用來表示何時停止的斷言附迷,一個在test前會執(zhí)行的操作,以及一個會返回對輸入進行處理的操作的函數(shù)哎媚。后兩個參數(shù)可以對任意的Monad適用喇伯,而不僅僅限制于IO Monad,這就是我們使用一個帶有類型限制Monad m =>的變量m來表示它的類型的原因拨与。

注意稻据,和遞歸函數(shù)一樣,我們也能夠定義遞歸的操作买喧。

現(xiàn)在所有的部件都已經(jīng)準(zhǔn)備完畢了捻悯,我們可以輕松寫出我們的REPL:

runRepl :: IO ()
runRepl = until_ (== "quit") (readPrompt "Lisp>>> ") evalAndPrint

修改我們的主函數(shù)匆赃,讓它既能夠執(zhí)行單個的表達式,也能夠進入一個會連續(xù)對輸入的表達式進行計算直達我們輸入quit才退出的REPL:

main :: IO ()
main = do args <- getArgs
          case length args of
               0 -> runRepl
               1 -> evalAndPrint $ args !! 0
               otherwise -> putStrLn "Program takes only 0 or 1 argument"

編譯并運行程序秋度,然后試試看:

$ ghc -package parsec -fglasgow-exts -o lisp [../code/listing7.hs listing7.hs]
$ ./lisp
Lisp>>> (+ 2 3)
5
Lisp>>> (cons this '())
Unrecognized special form: this
Lisp>>> (cons 2 3)
(2 . 3)
Lisp>>> (cons 'this '())
(this)
Lisp>>> quit
$
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炸庞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荚斯,更是在濱河造成了極大的恐慌埠居,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件事期,死亡現(xiàn)場離奇詭異滥壕,居然都是意外死亡,警方通過查閱死者的電腦和手機兽泣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門绎橘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唠倦,你說我怎么就攤上這事称鳞。” “怎么了稠鼻?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵冈止,是天一觀的道長。 經(jīng)常有香客問我候齿,道長熙暴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任慌盯,我火速辦了婚禮周霉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亚皂。我一直安慰自己俱箱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布孕讳。 她就那樣靜靜地躺著匠楚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪厂财。 梳的紋絲不亂的頭發(fā)上芋簿,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音璃饱,去河邊找鬼与斤。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撩穿。 我是一名探鬼主播磷支,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼食寡!你這毒婦竟也來了雾狈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤抵皱,失蹤者是張志新(化名)和其女友劉穎善榛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呻畸,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡移盆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了伤为。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咒循。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绞愚,靈堂內(nèi)的尸體忽然破棺而出叙甸,到底是詐尸還是另有隱情,我是刑警寧澤位衩,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布蚁署,位于F島的核電站,受9級特大地震影響蚂四,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哪痰,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一遂赠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晌杰,春花似錦跷睦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至爹殊,卻和暖如春蜕乡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梗夸。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工层玲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓辛块,卻偏偏與公主長得像畔派,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子润绵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容