Java使用Aviator表达式学习记录(二十)

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

seq.not_any

seq.not_any(seq, fn) 和 seq.every 正好相反,当且仅当 seq 里的每个元素满足 fn(x) == false 才返回 true,其他都返回 false,表示 seq 里没有一个元素满足特定谓词检查:

## seq.not_any
println("There are not any elements in array is less than zero: " 
        + seq.not_any(a, lambda(x) -> x < 0 end));
println("There are not any  in range is less than zero: " 
        + seq.not_any(r, lambda(x) -> x < 0 end));
复制代码
There are not any elements in array is less than zero: true
There are not any elements in range is less than zero: false
``


#### seq.some

`seq.some(seq, fn)`  返回 seq 中第一个使得 `fn(x) == true` 的元素,如果没有找到,返回 `nil` :


复制代码

seq.some

println("Find a element in array is greater than zero: "
+ seq.some(a, lambda(x) -> x > 0 end));
println("Find a element in range is greater than zero: "
+ seq.some(r, lambda(x) -> x > 0 end));
println("Find a element in list is starting with 'c': "
+ seq.some(n, lambda(x) -> string.startsWith(x, "c") end));

复制代码

Find a element in array is greater than zero: 1
Find a element in range is greater than zero: 1
Find a element in list is starting with 'c': car




#### take_while


`take_while(sequence, pred)` 用于从集合 sequence 里挑选出 `pred(元素)` 返回 `true` 的元素并返回新的集合:


复制代码

fn is_neg(x) {
x < 0
}

let list = seq.list(-2, -1, 0, 1, 2, 3, 0, 99, -1000, 7);
let result = take_while(list, is_neg);
p("result of take_while: #{result}");



通过 `take_while` 我们从 `list` 里挑选出所有的负数并打印:

复制代码

result of take_while: [-2, -1, -1000]



反过来,我们也可以“丢弃”所有的负数,这就要用到 `drop_while` 。


#### drop_while


复制代码

let result = drop_while(list, is_neg);

p("result of drop_while: #{result}");



从 `list` 里将所有的负数 drop 出去,剩下的都应该是非负数:


复制代码

result of drop_while: [0, 1, 2, 3, 0, 99, 7]


#### group_by


`group_by(sequence, keyfn)` 可以为集合做分组,它会将 `keyfn` 函数作用到集合里的每个元素上,返回分组的 key,然后返回相同 key 的将放在同一个 list 里,最终返回一个 map 映射: `{key1 -> [e1, e2], key2 -> [e3, e4], ...}` :


复制代码

let result = group_by(list, is_neg);
p("result of group_by: #{result}");



执行上面代码将输出:

复制代码

result of group_by: {false=[0, 1, 2, 3, 0, 99, 7], true=[-2, -1, -1000]}



`is_neg` 当遇到负数的时候返回 true,其他情况返回 false,因此最终集合就变成了两个分组。


#### distinct


`distinct(sequence)` 用于消除集合中的重复元素,返回没有重复的集合:


复制代码

let result = distinct(list);
p("result of distinct: #{result}");



输出

复制代码

result of distinct: [-2, -1, 0, 1, 2, 3, 99, -1000, 7]


可以看到 0 这个重复元素被移除了,只保留一个。


#### reverse



`reverse(sequence)` 用于返回集合的逆序结果,仅可作用于数组、List 等顺序集合:


复制代码

let result = reverse(list);
p("result of reverse: #{result}");


将输出 list 的逆序集合:

复制代码

list is: [-2, -1, 0, 1, 2, 3, 0, 99, -1000, 7]
......
result of reverse: [7, -1000, 99, 0, 3, 2, 1, 0, -1, -2]



#### zipmap


接下来我们将描述几个用于产生集合的函数,先从 `zipmap(list1, list2)` 开始,它是将两个集合按照 `e1-> e2` 的顺序映射成一个 map,我们看一个例子:


复制代码

let m = zipmap(tuple("a", "b", "c"), seq.list(1,2,3,4));
p("type of m: " + type(m));
p("result of zipmap: #{m}");



我们给 `zipmap` 分别传入了两个集合,最终生成一个 map:

复制代码

type of m: java.util.HashMap
result of zipmap: {a=1, b=2, c=3}



如果两个集合的长度不一样,将以最短的集合来截止:

复制代码

let m = zipmap(tuple("a", "b", "c"), seq.list(1,2,3,4, 5, 6));
p("result of zipmap: #{m}");


结果仍然是 `{a=1, b=2, c=3}` 。


#### concat


`concat(seq1, seq2)` 用于连接两个集合,生成一个新的集合,复杂度在 O(m+n),m 和 n 分别是两个集合的长度:


复制代码

let c = concat(tuple("a", "b", "c"), seq.list(1, 2, 3, 4));
p("result of concat: #{c}");


输出:

复制代码

result of concat: [a, b, c, 1, 2, 3, 4]



### 自定义 sequence


假设有这么一个场景,你从数据库查询 User 表拿到了一个 `java.sql.ResultSet` ,你想传入 AviatorScript 处理,并且想使用上面提到的各种函数,那么你可以为 `ResultSet` 实现一个 seq 包装:


复制代码

package com.googlecode.aviator.example.seq;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.googlecode.aviator.runtime.type.Collector;
import com.googlecode.aviator.runtime.type.Sequence;
import com.googlecode.aviator.runtime.type.seq.ListCollector;
import com.googlecode.aviator.utils.Reflector;

/**

*/
public class ResultSetSequence implements Sequence<Map<String, Object>> {
private final ResultSet resultSet;

public ResultSetSequence(final ResultSet resultSet) {
super();
this.resultSet = resultSet;
}

@Override
public Iterator<Map<String, Object>> iterator() {
return new Iterator<Map<String, Object>>() {

  @Override
  public boolean hasNext() {
    try {
      return ResultSetSequence.this.resultSet.next();
    } catch (SQLException e) {
      throw Reflector.sneakyThrow(e);
    }
  }

  @Override
  public Map<String, Object> next() {
    try {
      Map<String, Object> user = new HashMap<>();
      user.put("username", ResultSetSequence.this.resultSet.getString("username"));
      user.put("age", ResultSetSequence.this.resultSet.getString("age"));
      return user;
    } catch (SQLException e) {
      throw Reflector.sneakyThrow(e);
    }
  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException();
  }

};
复制代码

}

@Override
public Collector newCollector(final int size) {
return new ListCollector(false);
}

@Override
public int hintSize() {
// if we don't known the exact row number, return 0.
return 0;
}

}


核心就是 `iterator` 方法,我们在 `next` 中将一行的结果取出来,封装成一个 map 对象返回。


接下来你就可以将这个 `ResultSet` 包装后扔到 AvaitorScript 中处理:


复制代码

package com.googlecode.aviator.example.seq;

import java.sql.ResultSet;
import org.mockito.Mockito;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;

public class DemoResultSetSeq {

public static void main(final String[] args) throws Exception {
// Mock a result set.
ResultSet resultSet = Mockito.mock(ResultSet.class);
Mockito.when(resultSet.next()).thenReturn(true).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getString("username")).thenReturn("dennis").thenReturn("catty");
Mockito.when(resultSet.getInt("age")).thenReturn(30).thenReturn(20);

// Use it in aviator
Expression exp = AviatorEvaluator.getInstance().compileScript("examples/result_set_seq.av");
exp.execute(exp.newEnv("results", new ResultSetSequence(resultSet)));
复制代码

}
}



我们先用 mockito 模拟了一个 `ResultSet` ,它会返回两行:


复制代码

username, age

dennis, 30
catty, 20


然后将 resultSet 包装成 `ResultSetSequence` ,作为 `results` 变量传入脚本 `examples/result_set_seq.av` :


复制代码

examples/result_set_seq.av

let users = into(seq.list(), results);

println("User names: "

  • map(users, lambda(u) -> u.username end));

println("users that age is greater than 30: "

  • filter(users, lambda(u) -> u.age > 30 end));

println("Total age: "

  • reduce(users, lambda(n, u) -> n + u.age end, 0));


我们先用 `into` 函数,将结果从 `ResultSet` 提取出来,方便后续的操作,接下来我们用 map 获取用户的变量名列表,用 filter 过滤大于 30 岁的用户,用 reduce 求值总的年龄数字:


复制代码

User names: [dennis, catty]
users that age is greater than 30: []
Total age: 50

复制代码