数据结构成为小语言
數(shù)據(jù)結(jié)構(gòu)成為小語言
?
面向語言的開發(fā)并不一定意味著,一定要自己開發(fā)解析器或編譯器。這就是說,我們將在下一章學(xué)習(xí)創(chuàng)建解析器,然后,把這樣的解析器和本章介紹的方法照結(jié)合起來,去構(gòu)建一個簡單的編譯器。可以通過創(chuàng)建數(shù)據(jù)結(jié)構(gòu)和函數(shù)或模塊,就能夠做很多事,數(shù)據(jù)結(jié)構(gòu)描述了準備做什么,而函數(shù)或模塊定義了如何解釋結(jié)構(gòu)。
幾乎可以用任何語言創(chuàng)建數(shù)據(jù)結(jié)構(gòu)來表示一個程序,但是 F# 更適合。F# 的文字列表和數(shù)組很容易定義,不要求龐大的類型注解;它的聯(lián)合類型能夠創(chuàng)建結(jié)構(gòu),來表達相關(guān)的概念,但卻不一定要包含相同類型的數(shù)據(jù),可以用它來創(chuàng)建樹型結(jié)構(gòu),這在創(chuàng)建語言時是非常有用的;最后,由于函數(shù)可以看作是值,因此,我們可以很容易把函數(shù)嵌入到數(shù)據(jù)結(jié)構(gòu)中,這樣,F# 的表達式就能成為語言的一部分,通常作為一個動作,來響應(yīng)語言的特定條件。
我們先來看一下在FSharp.PowerPack.dll 中預(yù)定義的 DSL,通過 Arg(參數(shù))模塊能夠快速構(gòu)建命令行參數(shù)解析器。它是通過 F# 的聯(lián)合和列表類型而實現(xiàn)的,先創(chuàng)建小語言,然后,用參數(shù)模塊中的大量函數(shù)進行解釋。
參數(shù)模塊公開了一個元組類型argspec,它由兩個字符串和一個聯(lián)合類型 spec 組成。元組中的第一個字符串指定了命令行參數(shù)的名字,元組中的第二項是聯(lián)合類型 spec,它指定了參數(shù)的內(nèi)容,例如,它可以指定后面跟著的是字符串值,或者只是一個標(biāo)志;它還可以指定如果發(fā)現(xiàn)命令行標(biāo)記,應(yīng)該做什么。元組中最后的字符串是關(guān)于這個標(biāo)志做什么的文本描述,當(dāng)命令行參數(shù)錯誤時會打印到控制臺,對程序員來說也是一個有用的注釋。
參數(shù)模塊公開了兩個函數(shù)用于解析參數(shù):parse,它解析在命令行中傳入的命令;和parse_argv,它要求直接傳遞參數(shù)給它。我們應(yīng)該傳遞描述命令行參數(shù)的 argspec 類型列表的函數(shù),所有被傳遞給函數(shù)的命令行參數(shù)都沒有前綴 -,最后,是描述用法的字符串。
這個模塊還公開了第三個函數(shù),可以傳遞argspec 類型的列表,并直接寫出用法。
下面的例子演示了如何以這種方法構(gòu)建參數(shù)解析器,把從命令行收集的參數(shù)保存到標(biāo)識符中準備以后使用,這里,是把它們定到控制臺:
?
let myFlag = ref true
let myString = ref ""
let myInt = ref 0
let myFloat = ref 0.0
let (myStringList : string list ref) = ref []
?
let argList =
??? [ ("-set", Arg.Set myFlag, "Sets the value myFlag");
????? ("-clear", Arg.Clear myFlag, "Clears the value myFlag");
????? ("-str_val", Arg.String(fun x -> myString:= x), "Sets the value myString");
????? ("-int_val", Arg.Int(fun x -> myInt :=x), "Sets the value myInt");
????? ("-float_val", Arg.Float(fun x -> myFloat:= x), "Sets the value myFloat") ]
?
ifSystem.Environment.GetCommandLineArgs().Length <> 1 then
? Arg.parse
??? argList
??? (funx -> myStringList := x :: !myStringList)
??? "Argmodule demo"
else
? Arg.usage
??? argList
??? "Argmodule demo"
? exit1
?
printfn "myFlag: %b" !myFlag
printfn "myString: %s" !myString
printfn "myInt: %i" !myInt
printfn "myFloat: %f" !myFloat
printfn "myStringList: %A"!myStringList
?
當(dāng)運行前面的代碼,沒有命令行參數(shù),或者命令行參數(shù)錯誤時,程序會輸出下面的結(jié)果:
?
Arg module demo
? -set:Sets the value my_flag
? -clear:Clears the value my_flag
? -str_val<string>: Sets the value my_string
? -int_val<int>: Sets the value my_int
? -float_val<float>: Sets the value my_float
? --help:display this list of options
? -help:display this list of options
?
當(dāng)用下面的命令行運行前面的示例時:
?
args.exe -clear -str_val "helloworld" -int_val 10 -float_val 3.14 "file1" "file2""file3"::
?
輸出如下:
?
myFlag: false
myString: hello world
myInt: 10
myFloat: 3.140000
myStringList: ["file3";"file2"; "file1"]
?
我特別喜歡這樣的 DSL,因為它使問題更清晰,程序需要什么參數(shù),如果接收到參數(shù),應(yīng)該進行什么樣的處理。幫助文本也保存在這個結(jié)構(gòu)中,有兩個目的:如果命令行參數(shù)有錯誤,函數(shù)會自動輸出一個幫助消息;當(dāng)我們忘記什么參數(shù)時,可以提醒。我也喜歡用這種方式創(chuàng)建命令行解析器,因為,我用命令語言寫過幾個命令行解析器,那可不是令人滿意的體驗,最終必須寫大量的代碼,詳細說明如何拆分命令行。如果你用.NET 寫過代碼,那么,通常會花太多的時間在調(diào)用字符串類型的IndexOf 和Substring 方法。
總結(jié)
- 上一篇: 电压转换电路整理
- 下一篇: 区块链项目的价值评估关键词:数据|筱静观