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

2,137 阅读3分钟

这是我参与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