這兩天完善了我的jaskell語言的一個shell。
這個shell雖然是jaskell的,但是也可以作為一個交互式執行java代碼的解釋器。對于想快速地試試某個api比較有用。
相比于eclipse scrapebook,它的好處是更方便,而且,jaskell的一些函數式的特性讓你可以寫出更加簡潔的代碼。
下面舉幾個例子:
輕松玩Swing
打開shell,它顯示這樣:
Jaskell Shell version 0.5
>然后假設你要運行一下javax.swing.JOptionPane.showInputDialog()函數的話,你可以這樣寫:
結果就會出現一個簡單的swing對話框。在對話框里面輸入年齡“13",回車,
shell里面就會顯示:13。
這里面,一點需要注意的,java方法調用不用圓括號,而是方括號。你可以把這個理解為一個reflection調用,傳遞的永遠都是一個數組。
下面,假設你想重復地使用JOptionPane這個類,使用showInputDialog, showConfirmDialog這類的方法,總這么寫javax.swing.JOptionPane也夠麻煩的。我們可以簡化它:
下面我們可以重復使用dialog變量了。在這之前,我們可能想看看JOptionPane到底都支持什么靜態方法,我們可以用"?"來讓shell告訴我們
然后假設我們選擇showMessageDialog,可以這樣寫:
更簡化一點,假設我要重復showMessageDialog若干遍,我可以這樣寫:
下面你可以say很多東西啦:
傻瓜多線程
好,看過了JOptionPane,我們來看看多線程。下面是用這個語言怎么啟動一個線程:
構造函數也需要用一個list來傳遞參數。我們這里傳遞的是一個Runnable對象。
用來實現Runnable接口的是一個匿名函數,這個函數不管參數是什么,一旦調用,就println一下。
implements 是一個函數,它負責用一個函數來動態生成一個實現某接口的proxy出來。它前面的那個反向單引號表示把一個函數以中綴語法調用,所以 (somefunction `implements Runnable)等價于implements(somefunction, Runnable)。
"\"符號是lamda函數表示法。"->"符號前面的是函數參數,后面的是函數體。這里因為我們不使用這個參數,所以用"_"這個通配符。
最后,我們調用start[]方法來執行這個線程。
我們還可以用標準的"const"函數來讓代碼更簡短一點。"const x"語義上完全等價于"\_->x"。另外,我們也可以用java風格的new操作符函數來寫,jaskell對兩者都支持的:
然后,考慮到重用,我們可以這樣,先把System.out.println搞短一點,每次敲這么長太麻煩:
(實際上,println函數是系統已經缺省就定義好的了。你完全沒有必要自己定義println就可以直接用了。這里只是演示一下怎么自己定義函數)
然后,把那段啟動線程的代碼寫成函數:
好了,下面我們可以任意啟動線程做事情了:
最后一個say "nice!",如果不用單獨線程的話,運行后這個對話框將阻塞當前線程。現在用run來運行它,就不會阻塞了。
今天你fp了嗎?
最后再隨便看看jaskell作為函數式語言的本分所能做的一些事。
foreach函數:
map函數:
filter函數:
> filter(\x->x<10, list 1 100)
>
=> [1,2,3,4,5,6,7,8,9]這個代碼把1到100中所有小于10的整數都取出來。
filter函數的第一個參數是一個函數,這個函數對列表中的每一個元素都進行判斷,返回true或者false。
find函數:
lookup函數:
@函數:
上面我們知道找到的數字是在位置7,可以用@來得到位置7的值:
sum函數:
> sum(list 1 100)
>
=> 5050
注意,最難的來了!
fold函數:
(*)是一個乘法函數。
你也可以用自己寫的:
行了,差不多了。下載在:
http://dist.codehaus.org/yan/distributions/jaskell.zip
把jar文件都放到你的classpath里面,然后運行jfun.jaskell.shell.Shell類就行了。
這個shell雖然是jaskell的,但是也可以作為一個交互式執行java代碼的解釋器。對于想快速地試試某個api比較有用。
相比于eclipse scrapebook,它的好處是更方便,而且,jaskell的一些函數式的特性讓你可以寫出更加簡潔的代碼。
下面舉幾個例子:
輕松玩Swing
打開shell,它顯示這樣:
Jaskell Shell version 0.5
>
> javax.swing.JOptionPane.showInputDialog["your age?"]
>
回車兩下(第一下,就是簡單折行,因為你可以接著寫下一行代碼,只有連續兩下折行,shell才認為你是要執行)>
結果就會出現一個簡單的swing對話框。在對話框里面輸入年齡“13",回車,
shell里面就會顯示:13。
這里面,一點需要注意的,java方法調用不用圓括號,而是方括號。你可以把這個理解為一個reflection調用,傳遞的永遠都是一個數組。
下面,假設你想重復地使用JOptionPane這個類,使用showInputDialog, showConfirmDialog這類的方法,總這么寫javax.swing.JOptionPane也夠麻煩的。我們可以簡化它:
> dialog = javax.swing.JOptionPane
>
=> class javax.swing.JOptionPane
當你回車兩次后,shell在"=>"提示符后面自動顯示這個表達式的值:"class javax.swing.JOptionPane"。>
=> class javax.swing.JOptionPane
下面我們可以重復使用dialog變量了。在這之前,我們可能想看看JOptionPane到底都支持什么靜態方法,我們可以用"?"來讓shell告訴我們
> ? dialog
這個"?"不是jaskell語言的一部分,而是shell的命令,所以不需要回車兩次。回車,shell就會把JOptionPane的所有方法都列出來。然后假設我們選擇showMessageDialog,可以這樣寫:
> dialog.showMessageDialog[null, "hello world"]
>
然后,一個"hello world"的對話框就彈了出來(看不見?找一找。它可能被藏在你的當前窗口后面了。)>
更簡化一點,假設我要重復showMessageDialog若干遍,我可以這樣寫:
> say msg = dialog.showMessageDialog[null, msg]
>
=> say()
這個表達式的值是一個接受一個參數的,叫做say的函數。>
=> say()
下面你可以say很多東西啦:
> say "how are you?"
>
> say "java sucks!"
>
等等等等。>
> say "java sucks!"
>
傻瓜多線程
好,看過了JOptionPane,我們來看看多線程。下面是用這個語言怎么啟動一個線程:
> Thread.new[(\_->System.out.println["hello world"]) `implements Runnable].start[]
>
hello world
這里面Thread.new大概不需要解釋。這里對構造函數的調用是Ruby風格的ClassName.new,而不是java的new ClassName。>
hello world
構造函數也需要用一個list來傳遞參數。我們這里傳遞的是一個Runnable對象。
用來實現Runnable接口的是一個匿名函數,這個函數不管參數是什么,一旦調用,就println一下。
implements 是一個函數,它負責用一個函數來動態生成一個實現某接口的proxy出來。它前面的那個反向單引號表示把一個函數以中綴語法調用,所以 (somefunction `implements Runnable)等價于implements(somefunction, Runnable)。
"\"符號是lamda函數表示法。"->"符號前面的是函數參數,后面的是函數體。這里因為我們不使用這個參數,所以用"_"這個通配符。
最后,我們調用start[]方法來執行這個線程。
我們還可以用標準的"const"函數來讓代碼更簡短一點。"const x"語義上完全等價于"\_->x"。另外,我們也可以用java風格的new操作符函數來寫,jaskell對兩者都支持的:
> new Thread[const(System.out.println["hello world"]) `implements Runnable].start[]
>
hello world
>
hello world
然后,考慮到重用,我們可以這樣,先把System.out.println搞短一點,每次敲這么長太麻煩:
> println msg = System.out.println[msg]
>
=> println()
>
=> println()
(實際上,println函數是系統已經缺省就定義好的了。你完全沒有必要自己定義println就可以直接用了。這里只是演示一下怎么自己定義函數)
然后,把那段啟動線程的代碼寫成函數:
> run task = Thread.new[const task `implements Runnable].start[]
>
=> run()
>
=> run()
好了,下面我們可以任意啟動線程做事情了:
> run(println "hello world")
>
hello world
> run(println "pei!")
>
pei!
> run (say "nice!")
>
>
hello world
> run(println "pei!")
>
pei!
> run (say "nice!")
>
最后一個say "nice!",如果不用單獨線程的話,運行后這個對話框將阻塞當前線程。現在用run來運行它,就不會阻塞了。
今天你fp了嗎?
最后再隨便看看jaskell作為函數式語言的本分所能做的一些事。
foreach函數:
> foreach [1,2,"hello"] println
>
這個函數把1,2,"hello"三個都打印一遍。>
> foreach(list 1 100, println)
>
這個函數把數字1到100按順序打印一遍。>
map函數:
> map (\x->Integer.parseInt[x]) ["1","2","3"]
>
=> [1,2,3]
這個函數把一個字符串列表轉換成一個整數列表。>
=> [1,2,3]
filter函數:
> filter(\x->x<10, list 1 100)
>
=> [1,2,3,4,5,6,7,8,9]
filter函數的第一個參數是一個函數,這個函數對列表中的每一個元素都進行判斷,返回true或者false。
find函數:
> find(3, list 1 100)
>
=> 2
這個代碼在列表中尋找整數2,如果找到,返回找到的位置(0為第一個)>
=> 2
lookup函數:
> lookup(\x->x*x>x+50, list 1 100)
>
=> 7
這個代碼在列表中尋找第一個符合x*x>x+50的元素,找到就返回位置。>
=> 7
@函數:
上面我們知道找到的數字是在位置7,可以用@來得到位置7的值:
> list 1 100 @ 7
>
=> 8
好,這個數是8。>
=> 8
sum函數:
> sum(list 1 100)
>
=> 5050
注意,最難的來了!
fold函數:
> fold (*) 1 [1,2,3,4,5]
>
=> 120
這個fold函數以1為初始值,然后對每個列表元素,把它和當前值做乘法,用結果更新當前值,最后把計算結果返回。所以這段代碼實際上做的就是把從1到5乘起來。>
=> 120
(*)是一個乘法函數。
你也可以用自己寫的:
> fold (\x y->x*y) 1 [1,2,3,4,5]
一樣的。行了,差不多了。下載在:
http://dist.codehaus.org/yan/distributions/jaskell.zip
把jar文件都放到你的classpath里面,然后運行jfun.jaskell.shell.Shell類就行了。