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)
{
public:
    explicit FunctionStartsEndsWith(ContextPtr context) : selector(context)
    {
        selector.registerImplementation<TargetArch::Default,
            TargetSpecific::Default::FunctionStartsEndsWith<Name>>();
    #if USE_MULTITARGET_CODE
        selector.registerImplementation<TargetArch::SSE42,
            TargetSpecific::SSE42::FunctionStartsEndsWith<Name>>();
        selector.registerImplementation<TargetArch::AVX,
            TargetSpecific::AVX::FunctionStartsEndsWith<Name>>();
        selector.registerImplementation<TargetArch::AVX2,
            TargetSpecific::AVX2::FunctionStartsEndsWith<Name>>();
        selector.registerImplementation<TargetArch::AVX512F,
            TargetSpecific::AVX512F::FunctionStartsEndsWith<Name>>();
    #endif
    }
    ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
    {
        return selector.selectAndExecute(arguments, result_type, input_rows_count);
    }
    static FunctionPtr create(ContextPtr context)
    {
        return std::make_shared<FunctionStartsEndsWith<Name>>(context);
    }
private:
    ImplementationSelector<IFunction> selector;
};
}