This is the 21st day of my participation in the August Text Challenge.More challenges in August

Sequence

The Sequence of abstract

Sequence is AviatorScript’s abstraction of “collections”. This “collection” can include arrays, sets, maps, lists, etc., as long as it is a traversable collection. The concept of Sequence comes from Clojure, but it’s still a lot weaker than Clojure. For example, chunk/lazy is not supported.

In fact, Sequence simply inherits the Iterable interface:

/**
 * Sequence mark interface.
 *
 * @author dennis([email protected])
 *
 * @param <T>
 */
public interface Sequence<T> extends Iterable<T> {
  Collector newCollector(int size);

  int hintSize();
}
Copy the code

Two additional methods have been added:

  • hintSizeThe number of elements used to return the collection, which is only a hint, is not guaranteed to be exact.
  • newCollectorReturns the collector to collect the result of some “variation” of the elements in the sequence.

The Collector interface is also very simple:

/**
 * Collector to collect elements.
 *
 * @author dennis([email protected])
 *
 * @param <T>
 */
public interface Collector {
  void add(Object e);

  Object getRawContainer();
}
Copy the code
  • addMethod is used to add elements
  • getRawContainerReturns the actual underlying container.

For a bit of body, look at an internal Sequence implementation: ArraySequence, which converts an array into a Sequence.

The tuples, arrays, ranges, lists, maps, and sets you see in AviatorScript all implement corresponding sequences, which is why they can be manipulated using the same Set of apis.

We’ll look at these apis in detail below. Let’s start with traversal. See sequence2.av and sequence2.av for all examples.

Traversal sequence

The standard way to iterate over a Sequence is through a for loop, which we saw many examples of in the previous section:

## sequence.av

let a = seq.array(int, 1, 2, 3, 4);
let r = range(-5, 5);
let s = seq.set(99, 100, 101);
let m = seq.map("a", 1, "b", 2, "c", 3);
let n = seq.list("car", "bus", "bike");

## iterate elements
let sum = 0 ;
for e in r {
  sum = sum + e;
} 
println("sum of range r: " + sum);
for e in m {
  println(e.key + "=" + e.value);
}
Copy the code

For a Map, entries are iterated over. This section was covered in detail in the previous two sections on arrays and collections and will not be repeated.

A higher-order function that operates on a sequence

As for the abstraction of Sequence, AviatorScript also provides a set of higher-order functions to easily transform, filter, query, and aggregate collections, which we’ll cover in detail. The rule for these functions is to take sequence as the first argument.

count

The count(seq) function is used to get the set elements in seq. It will try to return the result within the time complexity of O(1), and at worst degenerate to O(n) :

## count
println("count of array: " + count(a));
println("count of range: " + count(r));
println("count of set: " + count(s));
println("count of map: " + count(m));
println("count of list: " + count(n));
Copy the code

is_empty

Is_empty is used to return whether the collection is empty, is_empty(nil) returns true:

println("is_empty(array): " + is_empty(a));
println("is_empty(seq.list()): " + is_empty(seq.list()));
println("is_empty(nil): " + is_empty(nil));
Copy the code

Output:

is_empty(array): false
is_empty(seq.list()): true
is_empty(nil): true
Copy the code

include

Include (seq, x) : include(seq, x) : include(seq, x) : include(seq, x) : include(seq, x)

## include
println("array has 3: " + include(a, 3));
println("map has an entry ('b', 2): " + include(m, seq.entry("b", 2)));
println("range has 10: " + include(r, 10));
Copy the code

Similarly, for maps, to compare map. Entry objects, you can use seq.entry(key, value) to construct Entry objects:

array has 3:true
map has an entry ('b', 2): true
range has 10:false
Copy the code

map

Map (seq, fn) is used to convert seq to another SEQ, which applies the function of the second parameter fn to each element in the collection. The result is collected into the other collection (which is where the collector mentioned above takes effect) and returns:

## map
let new_range = map(r, lambda(x) -> x + 1 end);
print("new range is: ");
for x in new_range {
   print(x);
   print(", ");
}
println()
let new_map = map(m, lambda(e) -> e.value = e.value + 100; return e; end);
println("new map is: " + new_map + ", and type is: "+ type(new_map));
Copy the code

New_range is the set of elements in range incremented by 1. New_map is the set of values of each entry in M incremented by 100.

new range is: -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 
new map is: [a=101, b=102, c=103], and type is: java.util.ArrayList
Copy the code

Lambda (e) -> e.value = e.value + 100; lambda(e) -> e.value + 100; return e; End returns e, which is map. Entry, so new_map results in an ArrayList of map. Entry objects, one by one, If we want to turn it into a HashMap we need to use the into function described below.

into

Into (to_seq, from_seq) is used to add from_seq elements one by one to the to_seq collection:

## into
let new_map = into(seq.map(), new_map);
println("new map is: " + new_map + ", and type is: "+ type(new_map));
Copy the code

We add each entry in the new_map list to the HashMap returned by seq.map() using seq.add(to_seq, entry) :

new map is: {a=101, b=102, c=103}, and type is: java.util.HashMap
Copy the code

We can also use it to convert collection types, such as arrays to sets:

let new_set = into(seq.set(), a);
println("new set is: " + new_set + ", and type is: "+ type(new_set));
Copy the code

Output:

new set is: [1, 2, 3, 4], and type is: java.util.HashSet
Copy the code

reduce

Reduce (seq, fn, init) is used to “aggregate” elements in SEq. During the first iteration, it calls the function of the second parameter and applies fn(init, element), the initial value of the third parameter, to each element. The returned result is called fn(result, Element) in subsequent iterations, and the reduce call is equivalent to the following code:

fn reduce(seq, fn, init) {
  let result = init;
  for element in seq {
    result = fn(result, element);
  }
  return result;
}
Copy the code

With reduce, we can easily sum arrays:

let sum_of_a = reduce(a, +, 0);
let sum_of_r = reduce(r, +, 0);
println("some of array is: " + sum_of_a);
println("some of range is: " + sum_of_r);
Copy the code

The + addition operator is also essentially a function.

You can count the total length of a string in a list:

let len = reduce(n, lambda(len, x) -> len  + count(x) end, 0);
println("total string length in list is: " + len);
Copy the code

In fact, you can also think of the map function as a reduce call:

fn mymap(seq, fn) {
  reduce(seq, 
         lambda(c, e) -> 
           seq.add(c, fn(e))
         end,
         seq.list())
}
println("test mymap: " + mymap(a, lambda(x) -> x * 2 end));
Copy the code

We use reduce to define our own map function — mymap. The initial value is the list returned by seq.list(). Each iteration we add the result of fn(element) to the final result list and finally return:

test mymap: [2, 4, 6, 8]
Copy the code

In fact, you can use reduce to define into, filter, etc. You can practice on your own if you are interested.

sort

Sort (seq) is only used to sort arrays or lists. Other seq types are invalid. Other collection types need to be converted into lists by functions such as into:

## sort
println("sort(list) is: " + sort(n));
println("sort(set) is: " + sort(into(seq.list(), s)));
Copy the code

Sort ends up calling collections.sort or arrays.sort.

Starting with 5.2, sort accepts a comparator argument that can be passed to a custom sort comparator, for example if we want to sort a List in reverse order:

let c = comparator(lambda(x, y) -> x > y end);
println("sort(list, c) is: " + sort(n, c));
Copy the code

The comparator function takes a predicate function for comparison, turns it into a java.util.Comparator object, passes it to sort, and finally outputs n in reverse order:

sort(list) is: [bike, bus, car]
sort(set) is: [99, 100, 101]
sort(list, c) is: [car, bus, bike]
Copy the code

filter

Filter (seq, fn) is used to filter a seq, which applies fn to each element and returns true to collect new seQs, otherwise discard:

## filter
let es = filter(r, lambda(x) -> x %2 == 0 end);
println("filter even number in range:"  + es);
let bs = filter(n, lambda(x) -> string.startsWith(x, "b") end);
println("bs is: "  + bs);
Copy the code

This will filter out the even numbers in the range, and filter out n from the list starting with string B:

filter even number in range:[-4, -2, 0, 2, 4]
bs is: [bus, bike]
Copy the code

The next three functions every/not_any/some are used to determine or find whether elements in SEq meet certain conditions.