前言
一直以來對Lisp語言懷有很崇敬的心里,《黑客與畫家》對Lisp更是推崇備至,雖然看了不少有關Lisp的介紹但都沒有機會去寫段程序試試,就像我對C++一樣,多少有點敬畏。這個周末花了不少時間來研究Lisp。
Lisp是古老的函數式語言,跟C,C++等命令式語言完全不一樣的編程風格,但Lisp的方言很多,最后Lisp標准委員制定了Common Lisp,但內容很長,有1000多頁,因此功能比較強大;而Lisp的另外一個主要分支就是Scheme,它的標准內容只有不到100頁,所以非常簡單,適合學術研究和大學計算機語言教學以及一般的工程應用。目前Lisp有在JVM上的實現,在.NET上的實現就是 IronScheme,於是我便開始選擇了IronScheme作為Lisp研究的第一站。
1,下載IronScheme源碼
IronScheme在Codeplex上有開源項目, https://ironscheme.codeplex.com/ ,可以下載它的源碼和編譯好的程序,在 https://ironscheme.codeplex.com/SourceControl/latest 可以下載源碼,我下載時候的文件名是 ironscheme-103684,下載的源碼可以用VS2008打開。如果沒有開發環境,直接用 debugbuild.bat 也就可以直接編譯。另外還可以直接運行測試 r6rstest.bat
2,IronScheme控制台
在網站上下載IronScheme的應用程序后,可以直接看到它已經提供了不同環境下的控制台程序,分別有64位與32位,.NET 2.0與4.0的程序: IronScheme.Console32-v2.exe IronScheme.Console32-v4.exe IronScheme.Console-v2.exe IronScheme.Console-v4.exe
2.1,執行Scheme程序
找一個合適的控制台運行下,輸入幾個Lisp表達式看看:

Lisp程序有一個天然的執行多個參數運算的特點,所以我們可以執行多個數字相加。也可以使用 display 函數顯示一個字符串。
2.2,中文亂碼問題
寫一個簡單的Hello 程序文件來加載 試試:

執行這個程序,成功 ,但是亂碼,不管是存儲成 ANSI格式還是UTF8格式均亂碼:

2.3,解決亂碼
無奈,只有打開IronScheme源碼進行分析,分析了很久很久....
最后干脆直接搜索編碼格式 Encoding...,好歹涉及這個關鍵詞的地方只有3個:
在 IronScheme.Console 項目下的 Program 文件中,找到下面的代碼:
Encoding oo = Console.OutputEncoding; EnableMulticoreJIT(); try { //Console.OutputEncoding = Encoding.UTF8; return IronSchemeConsoleHost.Execute(args); } finally { Console.OutputEncoding = oo; }
將原來的 Console.OutputEncoding = Encoding.UTF8 注釋即可,由於我的電腦是中文環境,這樣程序便以GBK的編碼運行了,此時即可正常顯示Scheme 程序中的 漢字。但是,如果要加載的文件名有漢字,則悲劇了,控制台無法輸入漢字...
再次檢查程序中所有跟控制台有關的編碼的地方,發現除了前面檢查過的編碼問題,再也沒有其它地方,最后跟蹤調試代碼,發現程序使用
Console.ReadKey()
方法來獲取屏幕輸入的,而這個方法,是無法獲得中文輸入的...%&*....
既然是截獲了鍵盤敲擊,那么我就頂一個特殊的鍵,按下它在彈窗出來一個窗口,在里面輸入中文就可以了吧,於是找到文件 SuperConsole.cs ,找到 Insert(ConsoleKeyInfo key) 方法,修改成下面的代碼:
private void Insert(ConsoleKeyInfo key) { char c; if (key.Key == ConsoleKey.F6) { Debug.Assert(FinalLineText.Length == 1); c = FinalLineText[0]; } else if (key.Modifiers == ConsoleModifiers.Alt && (key.Key >= ConsoleKey.NumPad0 && key.Key <= ConsoleKey.NumPad9)) { c = '?'; } else { c = key.KeyChar; } //Ctrl+Z 輸入漢字 if (key.Key == ConsoleKey.Z && key.Modifiers == ConsoleModifiers.Control) { frmInputString frm = new frmInputString(); frm.Activate(); frm.ShowDialog(); //Console.Write(frm.Text); string s = frm.Text; _input.Append(s); Output.Write(s); _rendered += s.Length; _current += s.Length; } else { Insert(c); } }
這樣就可以在Scheme控制台彈窗輸入中文了,順便加入文件選擇功能,方便加載程序文件,如圖:
控制台默認的字體是 “點陣字體”,這種字體在輸入中文后,Scheme 定位字符位置會有問題,應該使用非點陣字體,例如如下圖的設置(控制台窗口標題--屬性--字體):

3,Scheme 調用 .NET
按照 作者官方的說法,IronScheme是可以簽入在.NET應用程序里面的,但是單獨執行Scheme程序的時候,是否可以調用 .net已有的程序呢?這個IronScheme也提供了,下面是 https://ironscheme.codeplex.com/wikipage?title=clr-syntax&referringTitle=Documentation 頁面的內容:
These macro's are exported from the (ironscheme clr) library.
Common parameters
type is either:
- a symbol. Eg: Int32 or System.IO.Stream
- a list implying a generic type. Eg: (Action Int32)
- #f (false) meaning the type should try to be inferred
Primary syntax
(clr-namespaces) Returns all the imported at the lexical scope
(clr-reference reference) reference can be a symbol for the assembly short name (ie System.Web) or a string containing the fully qualified assembly name.
(clr-using namespace) namespace is a symbol. Eg System.IO .
(clr-call type method instance arg ...) method is a symbol for a simple name, eg ToInt32 or a string to resolve specific methods, eg "ToInt32(Object)" . instance is a reference to the object of type . Can be null ('()) for static methods. arg ... is the arguments passed to the method.
(clr-cast type expr) expr is the instance to be cast.
(clr-is type expr) expr is the instance to be tested.
(clr-new type arg ...) arg ... is the arguments passed to the constructor.
(clr-new-array type size) size is the size of the array. Must be an integer.
(clr-event-add! type event instance handler) event is a symbol for the name of the event. Eg Click . instance is a reference to the object of type . Can be null ('()) for static events. handler is a procedure taking the same number of arguments as the event's delegate.
(clr-event-remove! type event instance handler) event is a symbol for the name of the event. Eg Click . instance is a reference to the object of type . Can be null ('()) for static events. handler is a procedure taking the same number of arguments as the event's delegate.
(clr-field-get type field instance) field is a symbol for the name of the field. Eg m_foo . instance is a reference to the object of type . Can be null ('()) for static fields.
(clr-field-set! type field instance expr) field is a symbol for the name of the field. Eg m_foo . instance is a reference to the object of type . Can be null ('()) for static fields. expr is the value to set the field.
(pinvoke-call library method arg ...) arg ... is the arguments passed to the method.
Derived syntax
(clr-indexer-get type instance arg ...) instance is a reference to the object of type . arg ... is the arguments passed to the indexer.
(clr-indexer-set! type instance arg ... expr) instance is a reference to the object of type . arg ... is the arguments passed to the indexer. expr is the value to set the indexer.
(clr-prop-get type property instance) property is the name of the property. Eg Height . instance is a reference to the object of type . Can be null ('()) for static properties.
(clr-prop-set! type property instance expr) property is the name of the property. Eg Height . instance is a reference to the object of type . Can be null ('()) for static properties. expr is the value to set the property.
(clr-static-call type method arg ...) method is a symbol for a simple name, eg ToInt32 or a string to resolve specific methods, eg "ToInt32(Object)" . arg ... is the arguments passed to the method.
(clr-static-event-add! type event handler) event is a symbol for the name of the event. Eg Click . handler is a procedure taking the same number of arguments as the event's delegate.
(clr-static-event-remove! type event handler) event is a symbol for the name of the event. Eg Click . handler is a procedure taking the same number of arguments as the event's delegate.
(clr-static-field-get type field) field is a symbol for the name of the field. Eg m_foo .
(clr-static-field-set! type field expr) field is a symbol for the name of the field. Eg m_foo . expr is the value to set the field.
(clr-static-prop-get type property) property is the name of the property. Eg Height .
(clr-static-prop-set! type property expr) property is the name of the property. Eg Height . expr is the value to set the property.
3.1,小試牛刀
看來支持得還挺全面,馬上寫個程序試試看:
(import (rnrs) (ironscheme clr)) ;Define a function write-ln (define (write-ln fmt . args) (clr-static-call System.Console WriteLine (clr-cast System.String fmt) (clr-cast System.Object[] (list->vector args)))) ; And invoke it! (write-ln "{0}" "Hello World!") (write-ln "1:{0}" "aaa") (write-ln "1:{0} 2:{1}" "張三" "李四")
這個程序是調用 .net的 Console.WriteLine 方法的,運行這個程序試試:

注意程序文件需要保存為 UTF8格式的,IronScheme 才可以正常顯示中文。
3.2,為是么要用 Scheme調用 .NET?
利用 Lisp的強大表達能力,調用.net強大的類庫
Scheme可以當作腳本語言,可以.net程序動態生成一個 Scheme程序,Scheme程序再調用.net。。。。 這個過程的用途,明白了吧?
比如工作流程序,調用一個Scheme 腳本..
參考資源
更多的 Lisp,Scheme學習資源,可以參考下面的鏈接 :
Lisp 的永恆之道
http://www.oschina.net/question/28_57183
Scheme語言--簡介
http://blog.csdn.net/zzulp/article/details/5547729
學習Scheme
http://blog.csdn.net/ramacess/article/details/570769
Scheme 語言概要
http://www.ibm.com/developerworks/cn/linux/l-schm/index1.html
Read access to a .NET Assembly
https://ironscheme.codeplex.com/discussions/60977
Playing with IronScheme
http://www.codeproject.com/Articles/33050/Playing-with-IronScheme
尾遞歸
http://blog.sina.com.cn/s/blog_3e5694650100tzqq.html
本文程序下載 “IronScheme中文版”
http://pwmis.codeplex.com/releases/view/117822
---------分界線 ----------------
歡迎使用 PDF.NET SOD 開源框架,框架追求的目標是簡單與效率的平衡,體現在:代碼的精簡,開發、維護的簡單與追求極致的運行效率。
作者簡介:
本人現任職架構師,求伯樂,聯系方式:http://www.pwmis.com/sqlmap
