這兩天完善了我的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ù)的話,你可以這樣寫:
結(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也夠麻煩的。我們可以簡化它:
下面我們可以重復(fù)使用dialog變量了。在這之前,我們可能想看看JOptionPane到底都支持什么靜態(tài)方法,我們可以用"?"來讓shell告訴我們
然后假設(shè)我們選擇showMessageDialog,可以這樣寫:
更簡化一點(diǎn),假設(shè)我要重復(fù)showMessageDialog若干遍,我可以這樣寫:
下面你可以say很多東西啦:
傻瓜多線程
好,看過了JOptionPane,我們來看看多線程。下面是用這個(gè)語言怎么啟動(dòng)一個(gè)線程:
構(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ì)兩者都支持的:
然后,考慮到重用,我們可以這樣,先把System.out.println搞短一點(diǎn),每次敲這么長太麻煩:
(實(shí)際上,println函數(shù)是系統(tǒng)已經(jīng)缺省就定義好的了。你完全沒有必要自己定義println就可以直接用了。這里只是演示一下怎么自己定義函數(shù))
然后,把那段啟動(dòng)線程的代碼寫成函數(shù):
好了,下面我們可以任意啟動(dòng)線程做事情了:
最后一個(gè)say "nice!",如果不用單獨(dú)線程的話,運(yùn)行后這個(gè)對(duì)話框?qū)⒆枞?dāng)前線程。現(xiàn)在用run來運(yùn)行它,就不會(huì)阻塞了。
今天你fp了嗎?
最后再隨便看看jaskell作為函數(shù)式語言的本分所能做的一些事。
foreach函數(shù):
map函數(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ù):
lookup函數(shù):
@函數(shù):
上面我們知道找到的數(shù)字是在位置7,可以用@來得到位置7的值:
sum函數(shù):
> sum(list 1 100)
>
=> 5050
注意,最難的來了!
fold函數(shù):
(*)是一個(gè)乘法函數(shù)。
你也可以用自己寫的:
行了,差不多了。下載在:
http://dist.codehaus.org/yan/distributions/jaskell.zip
把jar文件都放到你的classpath里面,然后運(yùn)行jfun.jaskell.shell.Shell類就行了。
這個(gè)shell雖然是jaskell的,但是也可以作為一個(gè)交互式執(zhí)行java代碼的解釋器。對(duì)于想快速地試試某個(gè)api比較有用。
相比于eclipse scrapebook,它的好處是更方便,而且,jaskell的一些函數(shù)式的特性讓你可以寫出更加簡潔的代碼。
下面舉幾個(gè)例子:
輕松玩Swing
打開shell,它顯示這樣:
Jaskell Shell version 0.5
>
> 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"。>
=> 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很多東西啦:
> say "how are you?"
>
> say "java sucks!"
>
等等等等。>
> 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。>
hello world
構(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
>
hello world
然后,考慮到重用,我們可以這樣,先把System.out.println搞短一點(diǎn),每次敲這么長太麻煩:
> println msg = System.out.println[msg]
>
=> println()
>
=> println()
(實(shí)際上,println函數(shù)是系統(tǒng)已經(jīng)缺省就定義好的了。你完全沒有必要自己定義println就可以直接用了。這里只是演示一下怎么自己定義函數(shù))
然后,把那段啟動(dòng)線程的代碼寫成函數(shù):
> run task = Thread.new[const task `implements Runnable].start[]
>
=> run()
>
=> run()
好了,下面我們可以任意啟動(dòng)線程做事情了:
> run(println "hello world")
>
hello world
> run(println "pei!")
>
pei!
> run (say "nice!")
>
>
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ù)列表。>
=> [1,2,3]
filter函數(shù):
> filter(\x->x<10, list 1 100)
>
=> [1,2,3,4,5,6,7,8,9]
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è))>
=> 2
lookup函數(shù):
> lookup(\x->x*x>x+50, list 1 100)
>
=> 7
這個(gè)代碼在列表中尋找第一個(gè)符合x*x>x+50的元素,找到就返回位置。>
=> 7
@函數(shù):
上面我們知道找到的數(shù)字是在位置7,可以用@來得到位置7的值:
> list 1 100 @ 7
>
=> 8
好,這個(gè)數(shù)是8。>
=> 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乘起來。>
=> 120
(*)是一個(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類就行了。