这是我参与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;
/**
- A sequence wraps java.seql.ResultSet
- @author dennis(killme2008@gmail.com)
*/ 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