這兩天完善了我的jaskell語言的一個(gè)shell。

這個(gè)shell雖然是jaskell的,但是也可以作為一個(gè)交互式執(zhí)行java代碼的解釋器。對(duì)于想快速地試試某個(gè)api比較有用。

相比于eclipse scrapebook,它的好處是更方便,而且,jaskell的一些函數(shù)式的特性讓你可以寫出更加簡潔的代碼。

下面舉幾個(gè)例子:

輕松玩Swing

打開shell,它顯示這樣:

Jaskell Shell version 
0.5
>
然后假設(shè)你要運(yùn)行一下javax.swing.JOptionPane.showInputDialog()函數(shù)的話,你可以這樣寫:
> javax.swing.JOptionPane.showInputDialog["your age?"]
>
回車兩下(第一下,就是簡單折行,因?yàn)槟憧梢越又鴮懴乱恍写a,只有連續(xù)兩下折行,shell才認(rèn)為你是要執(zhí)行)
結(jié)果就會(huì)出現(xiàn)一個(gè)簡單的swing對(duì)話框。在對(duì)話框里面輸入年齡“13",回車,
shell里面就會(huì)顯示:13。


這里面,一點(diǎn)需要注意的,java方法調(diào)用不用圓括號(hào),而是方括號(hào)。你可以把這個(gè)理解為一個(gè)reflection調(diào)用,傳遞的永遠(yuǎn)都是一個(gè)數(shù)組。

下面,假設(shè)你想重復(fù)地使用JOptionPane這個(gè)類,使用showInputDialog, showConfirmDialog這類的方法,總這么寫javax.swing.JOptionPane也夠麻煩的。我們可以簡化它:
> dialog = javax.swing.JOptionPane
>
=> class javax.swing.JOptionPane
當(dāng)你回車兩次后,shell在"=>"提示符后面自動(dòng)顯示這個(gè)表達(dá)式的值:"class javax.swing.JOptionPane"。

下面我們可以重復(fù)使用dialog變量了。在這之前,我們可能想看看JOptionPane到底都支持什么靜態(tài)方法,我們可以用"?"來讓shell告訴我們
> ? dialog
這個(gè)"?"不是jaskell語言的一部分,而是shell的命令,所以不需要回車兩次。回車,shell就會(huì)把JOptionPane的所有方法都列出來。

然后假設(shè)我們選擇showMessageDialog,可以這樣寫:
> dialog.showMessageDialog[null"hello world"]
>
然后,一個(gè)"hello world"的對(duì)話框就彈了出來(看不見?找一找。它可能被藏在你的當(dāng)前窗口后面了。)

更簡化一點(diǎn),假設(shè)我要重復(fù)showMessageDialog若干遍,我可以這樣寫:
> say msg = dialog.showMessageDialog[null, msg]
>
=> say()
這個(gè)表達(dá)式的值是一個(gè)接受一個(gè)參數(shù)的,叫做say的函數(shù)。

下面你可以say很多東西啦:
> say "how are you?"
>

> say "java sucks!"
>
等等等等。



傻瓜多線程

好,看過了JOptionPane,我們來看看多線程。下面是用這個(gè)語言怎么啟動(dòng)一個(gè)線程:
> Thread.new[(\_->System.out.println["hello world"]) `implements Runnable].start[]
>
hello world
這里面Thread.new大概不需要解釋。這里對(duì)構(gòu)造函數(shù)的調(diào)用是Ruby風(fēng)格的ClassName.new,而不是java的new ClassName。

構(gòu)造函數(shù)也需要用一個(gè)list來傳遞參數(shù)。我們這里傳遞的是一個(gè)Runnable對(duì)象。
用來實(shí)現(xiàn)Runnable接口的是一個(gè)匿名函數(shù),這個(gè)函數(shù)不管參數(shù)是什么,一旦調(diào)用,就println一下。

implements 是一個(gè)函數(shù),它負(fù)責(zé)用一個(gè)函數(shù)來動(dòng)態(tài)生成一個(gè)實(shí)現(xiàn)某接口的proxy出來。它前面的那個(gè)反向單引號(hào)表示把一個(gè)函數(shù)以中綴語法調(diào)用,所以 (somefunction `implements Runnable)等價(jià)于implements(somefunction, Runnable)。

"\"符號(hào)是lamda函數(shù)表示法。"->"符號(hào)前面的是函數(shù)參數(shù),后面的是函數(shù)體。這里因?yàn)槲覀儾皇褂眠@個(gè)參數(shù),所以用"_"這個(gè)通配符。

最后,我們調(diào)用start[]方法來執(zhí)行這個(gè)線程。


我們還可以用標(biāo)準(zhǔn)的"const"函數(shù)來讓代碼更簡短一點(diǎn)。"const x"語義上完全等價(jià)于"\_->x"。另外,我們也可以用java風(fēng)格的new操作符函數(shù)來寫,jaskell對(duì)兩者都支持的:
> new Thread[const(System.out.println["hello world"]) `implements Runnable].start[]
>
hello world


然后,考慮到重用,我們可以這樣,先把System.out.println搞短一點(diǎn),每次敲這么長太麻煩:
> println msg = System.out.println[msg]
>
=> println()

(實(shí)際上,println函數(shù)是系統(tǒng)已經(jīng)缺省就定義好的了。你完全沒有必要自己定義println就可以直接用了。這里只是演示一下怎么自己定義函數(shù))


然后,把那段啟動(dòng)線程的代碼寫成函數(shù):
> run task = Thread.new[const task `implements Runnable].start[]
>
=> run()

好了,下面我們可以任意啟動(dòng)線程做事情了:
> run(println "hello world")
>
hello world

> run(println "pei!")
>
pei
!

> run (say "nice!")
>

最后一個(gè)say "nice!",如果不用單獨(dú)線程的話,運(yùn)行后這個(gè)對(duì)話框?qū)⒆枞?dāng)前線程。現(xiàn)在用run來運(yùn)行它,就不會(huì)阻塞了。


今天你fp了嗎?

最后再隨便看看jaskell作為函數(shù)式語言的本分所能做的一些事。

foreach函數(shù):

> foreach [1,2,"hello"] println
>
這個(gè)函數(shù)把1,2,"hello"三個(gè)都打印一遍。

> foreach(list 1 100, println)
>
這個(gè)函數(shù)把數(shù)字1到100按順序打印一遍。

map函數(shù):
> map (\x->Integer.parseInt[x]) ["1","2","3"]
>
=> [
1,2,3]
這個(gè)函數(shù)把一個(gè)字符串列表轉(zhuǎn)換成一個(gè)整數(shù)列表。


filter函數(shù):


> filter(\x->x<10, list 1 100)
>
=> [
1,2,3,4,5,6,7,8,9]
這個(gè)代碼把1到100中所有小于10的整數(shù)都取出來。

filter函數(shù)的第一個(gè)參數(shù)是一個(gè)函數(shù),這個(gè)函數(shù)對(duì)列表中的每一個(gè)元素都進(jìn)行判斷,返回true或者false。

find函數(shù):

> find(3, list 1 100)
>
=> 2
這個(gè)代碼在列表中尋找整數(shù)2,如果找到,返回找到的位置(0為第一個(gè))


lookup函數(shù):

> lookup(\x->x*x>x+50, list 1 100)
>
=> 7
這個(gè)代碼在列表中尋找第一個(gè)符合x*x>x+50的元素,找到就返回位置。

@函數(shù):

上面我們知道找到的數(shù)字是在位置7,可以用@來得到位置7的值:

> list 1 100 @ 7
>
=> 8
好,這個(gè)數(shù)是8。


sum函數(shù):

> sum(list 1 100)
>
=> 5050


注意,最難的來了!
fold函數(shù):

> fold (*1 [1,2,3,4,5]
>
=> 120
這個(gè)fold函數(shù)以1為初始值,然后對(duì)每個(gè)列表元素,把它和當(dāng)前值做乘法,用結(jié)果更新當(dāng)前值,最后把計(jì)算結(jié)果返回。所以這段代碼實(shí)際上做的就是把從1到5乘起來。
(*)是一個(gè)乘法函數(shù)。

你也可以用自己寫的:
>  fold (\x y->x*y) 1 [1,2,3,4,5]
一樣的。


行了,差不多了。下載在:

http://dist.codehaus.org/yan/distributions/jaskell.zip

把jar文件都放到你的classpath里面,然后運(yùn)行jfun.jaskell.shell.Shell類就行了。