Support for arrays in `startsWith` (and, maybe, `endsWith`)

**Use case**

Would be quite useful for handling tree-like structured data where the node paths are specified as arrays of names or IDs.

**Describe the solution you'd like**

The desired behavior would be like this:
```
startsWith([1, 2, 3, 4], [1, 2])  -- true (1)
startsWith(['qwe', 'rty', 'ui', 'op'], ['qwe', 'rty'])  -- true (1)
startsWith([1, 2, 3, 4], [2, 4])  -- false (0)
startsWith([1, 1, 2, 2], [1, 2])  -- false (0)
```

And similarly, for `endsWith`

**Describe alternatives you've considered**

Something like `arraySlice(first, 1, length(second)) = second`

But `startsWith` would be much more readable, harder to make a mistake in and, probably, can be better optimized (i.e. might not have to create new objects like `arraySlice` does)
size_t secondCur = 0;
    while (firstCur < first.size && secondCur < second.size)
    {
        const bool is_first_null = has_first_null_map && first_null_map[firstCur];
        const bool is_second_null = has_second_null_map && second_null_map[secondCur];
        const bool cond_both_null_match = is_first_null && is_second_null;
        const bool cond_both_not_null = !is_first_null && !is_second_null;
        if (cond_both_null_match || (cond_both_not_null && isEqual(first, second, firstCur, secondCur)))
        {
            ++firstCur;
            ++secondCur;
        }
        else if (secondCur > 0)
        {
            secondCur = prefix_function[secondCur - 1];
        }
        else
        {
            ++firstCur;
        }
    }
    return secondCur == second.size;
}
template <
    ArraySearchType search_type,
    typename FirstSliceType,
    typename SecondSliceType,
    bool (*isEqual)(const FirstSliceType &, const SecondSliceType &, size_t, size_t),
    bool (*isEqualSecond)(const SecondSliceType &, size_t, size_t)>
bool sliceHasImpl(const FirstSliceType & first, const SecondSliceType & second, const UInt8 * first_null_map, const UInt8 * second_null_map)