什么是集合
至此,我们在前文中已经介绍了元组、列表、区间,以及Java中的集合对象(此处仅限于Java中的数组以及所有实现java.util.List的类),在ELite中,我们把这一类对象称之为集合,并且,ELite针对集合提供了丰富的内置函数。
length(size)、first、last
集合对象除了拥有其原有的属性与方法外,还拥有四个额外属性,分别是:length、size(等价于length)、first、last。
> define t1 = (1,2,3);
> t1.length;
3
> t1.first
1
> t1.last
3
>
> define lst1 = [1,2,3];
> lst1.length;
3
> lst1.first;
1
> lst1.last;
3
>
> define q = [1,3..10];
> q.size;
5
> q.first;
1
> q.last;
9
>
> define array = new int[]{1,2,3};
> array.length;
3
> array.first;
1
> array.last;
3
>
> define lst2 = new ArrayList();
> lst2.add(1);
true
> lst2.add(2);
true
> lst2.add(3);
true
> lst2.size;
3
> lst2.first;
1
> lst2.last;
3
>
同时,ELite提供了一些方便的集合操作函数,这些函数通常以一个lambda表达式或闭包为右操作数,闭包将对集合中的元素进行操作,以完成投影、选择、查找等操作。
asList
asList函数用于将某种“对象”转换成一个列表(List),通常我们用于:将元组、数组、或所有实现java.util. Iterable接口的对象,转换成列表。
> define lst1 = (1,2,3).asList();
> lst1;
[1, 2, 3]
> define lst2 = (new int[]{1,2,3}).asList();
> lst2;
[1, 2, 3]>
>
reverse
reverse函数将产生一个和原始集合元素排列顺序相反的集合。
> [1,2,3].reverse(); [3, 2, 1] >
sort
sort函数将对集合进行排序。
> [1,5,2,4].sort(); [1, 2, 4, 5] > ["a", "d", "e","0"].sort(); ['0', 'a', 'd', 'e'] >
可以为sort函数提供一个Lambda表达式作为其比较参数,该表达式具备两个参数,返回值可以为负数、零、正数,分别代表第一个参数小于、等于、大于第二个参数。
如:让一个int数组逆序排列:
> [1,3,2].sort({x,y=>y-x});
[3, 2, 1]
>
当然,你也可以将上述的Lambda表达式定义成一个变量:
> define sortMethod(x,y){y-x};
> [1,3,2].sort(sortMethod);
[3, 2, 1]
>
find
find函数接受一个Lambda表达式作为其参数,并集合中符合判断条件的第一个元素。
如:返回集合中第一个负数。
> [1,-1,2,-3].find({x=>x<0})
-1
>
map
map函数可以接受一个Lambda表达式作为参数,并用此Lambda表达式计算集合中的每一个元素,将结果组成一个新的集合。
> [1,2,3].map( {x => x*x })
[1, 4, 9]
>
一个显而易见的事实是:作为参数的闭包只能接受一个参数。
filter
filter函数用来遍历集合的每个元素, 调用给定的谓词, 当谓词返回值为true时将元素加入结果集合。
> define lst = [1,2..10];
> lst = lst.filter( { x => x % 2 == 0 } );
[2, 4, 6, 8, 10]
>
iterate
iterate函数具有两种形式,第一种形式为collection.iterate(closure),将使用闭包按顺序调用集合中的每一个元素,计算的结果将被丢弃。这种形式的iterate函数通常用于对集合元素进行加工处理。请注意,此时的iterate函数与map函数是有差异的,在map函数中,将闭包的返回值组成一个新的新合,而iterate函数将丢弃返回值。至于经过闭包处理后,集合的值是否变化,依赖于集合中的元素所引用的对象的变化。
此处举一个例子,但由于例子过长,因此,我们将示例代码放到一个文件iterate.xel中:
例 38.1. iterate.xel
class Employee {
define name;
define salary;
Employee(name, salary) {
this.name = name;
this.salary = salary;
}
public toString() {
return "${name}:${salary}";
}
}
define emps = [new Employee("Mary", 5000),
new Employee("Kevin", 4000), new Employee("Daniel", 10000)];
现在,我们在交互式窗口中运行:
> require "../sample/iterate.xel" > > emps; [Mary:5000, Kevin:4000, Daniel:10000] >
现在,我们需要将emps这个集合中所有员工的薪水(salary)普调10%:
> emps.iterate({x => x.salary *= 1.1});
> emps;
[Mary:5500.0, Kevin:4400.0, Daniel:11000.0]
>
第二种形式是collection.iterate(initial, closure),第一个参数为初始值,第二个参数是一个闭包,并且,该闭包必须具有两个形式参数,其中第一个参数是对上一个集合元素计算的结果(当对第一个元素计算时是initial参数的值),第二个元素是集合的当前元素,对最后一个元素的计算结果即为最终结果。采用这种形式可以方便地对集合元素进行汇总计算。
考虑到我们要计算某数值的阶乘,我们可以定义一个函数:
> define fact(x) {\
2) x <= 1 ? 1: x*fact(x-1);\
3) }
>
> fact(10);
3628800
>
上述阶乘形式,可以改造成 iterate 函数:
> [1..10].iterate(1, {result,n => result * n});
3628800
>
为了达到更好的重用,我们可以把上述代码声明成一个函数:
> define fact_ite(x) {[1..x].iterate(1, {result,n => result * n})};
> fact_ite(10);
3628800
>
使用iterate函数很容易实现循环算法,但会比递归拥有更高的效率。如上述示例,以递归形式完成的阶乘,和通过iterate函数完成的阶乘,两者之间是等价的,但通过iterate函数完成的阶乘,无疑效率更高。
但同时需要说明的是,在ELite中,同样实现了尾递归优化,因此,在ELite中,递归也不再是传统意义上的“洪水猛兽”。