这两天完善了我的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类就行了。