Named arguments. Overloading / Habr

Named arguments. Overloading / Habr

This article provides the most useful examples of loading based on argument names that I have encountered in my practice and which I implemented in

11 l

. It’s just that I won’t talk about named function/method arguments here: their benefit or harm is debatable, and support in programming languages ​​ranges from almost completely ignored (hello, C++) to outright

abuse\overuse

this option (hello Swift).

Overloading functions based on argument names is the ability to declare functions that have the same name and, as a rule, the same number of arguments, but which [аргументы] distinguished by their name [тип аргументов при этом может быть одинаковым]. At the same time, when calling such a function, arguments [те, которые отличаются только своим именем] should be called, otherwise it is not clear which overloaded variant of the function should be chosen.


Constructors

The biggest benefit, in my opinion, of loading based on argument names is for constructors. Because if for normal functions/methods it is quite possible to do with a suffix to the name of the function/method

[например, index(of: 1) можно заменить на indexOf(1), а index(where: ...) — на indexWhere(...)]

then this is much worse for designers:

charFromDigit

or

intToStringWithRadix

/

stringFromIntWithRadix

they look bulky and ugly.

The symbol constructor in 11l has the following forms:

  • Char(code' 65) – Creates a character with code 65 [т.е. соответствующий латинской букве A];
  • Char(digit' i) — creates a character that corresponds to a digit of a number i (at the same time, the number i must be integral and unequivocal, i.e. from 0 to 9 inclusive, otherwise the constructor generates an exception), is a shortened form of writing an expression Char(code' ‘0’.code + i) [плюс проверка на принадлежность i диапазону 0..9];
  • Char(string' s) – Creates a character from a single-character string s (if string s is empty or contains more than one character, an exception is generated). Can be useful when a character is created from a string received from the user [через консольный ввод, аргумент командной строки или из конфигурационного файла]to avoid adding a string length check manually.

Such constructors increase the readability of the symbol creation code by completely eliminating ambiguities:

Char(code' 0)

creates a character with code 0

[также можно использовать более короткую запись: "\0"]

and

Char(digit' 0)

produces a character corresponding to the digit 0 (ie code 48), while

Char(0)

is a compilation error {because

Char(0)

can be perceived in two ways: on the one hand, in C/C++ languages

char(0)

means a character with code 0, and on the other hand

String(0)

/

str(0)

is a string consisting of the digit character 0, and it would be logical if

Char(0)

kept the same behavior}.

What is the use of a separate symbol type, and why the C++ approach

[при котором char по факту совпадает с типом int8_t]

is an unsuccessful decision, I wrote in detail

here

.

Designer Int has the following forms:

  • Int(Float f) – creates an integer from a real number f by discarding the fractional part;
  • Int(String s) – creates an integer from a string s;
  • Int(s, radix' base) – creates an integer from a string s with a given base of the number system;
  • Int(bytes' b) – Creates an integer from a byte array b (order from youngest to oldest [little-endian]);
  • Int(bytes_be' b) – Creates an integer from a byte array b (order from oldest to youngest [big-endian]).

Designer

String

has the following forms:

  • String(o) – Creates a string representation of an object o;
  • String(i, radix' base) – Creates a string from a number i with a given base of the number system.

If C++ could use named arguments, then

std::vector

it would be more logical to construct as follows:

std::vector<int> a(size: 10); // вместо std::vector<int> a(10);
std::vector<int> b(reserve: 10); // ну или b(capacity: 10);

The split() method in strings

For example, it is necessary to analyze the BBCode-like markup.

Here are some of the supported tags.

  • [b]fatty[/b]
  • [url]http://…[/url]
  • [url=http://…]link[/url]
  • [img]http://…[/img]
  • [img=50,40]http://…[/img] (image with specified width and height)
  • [color=red]red[/color]
  • [color=255,0,0]also red[/color]
  • [color=255,0,0,128]red translucent[/color]

Parsing can be sold by character-by-character scanning of marked-up text. When an opening square bracket character is encountered, the matching closing bracket must be found and then checked to see if the substring enclosed in the square brackets is a valid tag. The first thing to do is to split such a string with a symbol

=

. In Python it looks like this:

tag_arr = tag_str.split('=', maxsplit = 1)

Note the named argument

maxsplit

method

split

. Value

1

means that the string

tag_str

will be split only by the first character found

=

which allows correct parsing of species tags

[url=https://www.google.com/search?q=test]

.

[В 11l аналогом аргумента maxsplit из Python является limit, причём maxsplit = 1 соответствует limit' 2. Такое поведение присуще Ruby и PHP и видится мне более естественным.]

Then you need to check the value tag_arr[0]:

match tag_arr[0]:
    case 'b':
        ...
    case 'url':
        ...
    case 'img':
        if len(tag_arr) == 2:
            size = tag_arr[1].split(',')
            assert(len(size) == 2)
            sizex, sizey = map(int, size)
            ...
        ...
    case 'color':
        if len(tag_arr) != 2:
            raise TextParseError(...)
        color = tag_arr[1].split(',')
        assert(len(color) in (1, 3, 4))
        if len(color) == 1:
            ...
        else:
            color_components = list(map(int, color))
            ...

Line pairs in Python:

color = tag_arr[1].split(',')
assert(len(color) in (1, 3, 4))

can be combined into one line at 11l using a named argument

req

:

var color = tag_arr[1].split(‘,’, req' (1, 3, 4))

At the same time, optimization is possible based on the fact that the maximum possible length of the array of lines

[либо массива из StringView]

which is returned by the method

split

equals 4. In other words, this method call

split

can return not a dynamic array, but a static one, for which memory is allocated on the stack

.

Similarly, these 3 lines in Python:

size = tag_arr[1].split(',')
assert(len(size) == 2)
sizex, sizey = map(int, size)

can be combined into one line on 11l:

var (sizex, sizey) = tag_arr[1].split(‘,’, req' 2).map(Int)

Generally speaking, the use of asserts

[как явных, так и неявных внутри метода split(..., req' ...)]

in the parser code is not the best idea, but quite viable: you can simply wrap the tag parsing code in a try-catch block and, when an exception occurs in the development assembly, show a MessageBox with an error message

[с кнопками ‘Продолжить’ и ‘Debug break’]

and in the final build for end users to ignore the incorrect tag or highlight it in red.

[При этом падать приложение из-за ошибки в теге, разумеется, не должно в любом случае.]

You can, of course, carefully process all possible errors in tags. I see – the documentation for all tags supported by the parser must be mandatory, and it is quite enough to indicate in which tag of the marked text there is an error – a look at the description of this tag in the documentation will, as a rule, be enough to find and correct the error.

Except limit and req variants of the method splitthere is more in 11l String.split(d, ', max) [который по сути равнозначен String.split(d, req' 1..max)] and String.split(d, ', first)which returns only the first first elements of the string array.

Of course, you could just add methods split_limit(), split_req(), split_max() and split_first()but overloaded split() with named arguments is still more beautiful.

The min() and max() functions

As in Python, functions

min()

and

max()

in 11l have an optional named argument

key

.

The following code will print the longest string in the array

arr

:

var arr = [‘a’, ‘bc’]
print(max(arr, key' s -> s.len))

Named template arguments/parameters

IN

attempts

add a static array class

[массива, максимальная длина которого известна на этапе компиляции]

in 11l, I made an unexpected discovery. Namely, that template parameters can be named similarly to function arguments.

[Впрочем, поиском ‘named template parameters’ выяснилось, что не мне первому пришла в голову эта идея.]

Here is an example of declaring a static array in 11l:

Array[Int, max_len' 10] static_array
[Int, max_len' 10] static_array2 // сокращённая форма [тип такой же]

And similarly, an array of fixed size:

Array[Int, len' 10] fixed_array
[Int, len' 10] fixed_array2 // сокращённая форма [тип такой же]

[Я предпочитаю len, а не size, т.к. последний у меня ассоциируется с размером в байтах (например, в языке Си sizeof(a) для массива из 10 целых 32-разрядных чисел возвращает 40, а не 10).]

Alternatively, a fixed-size array initialized with elements can be declared like this:

var fixed_array1 = -[1, 2, 3]
var fixed_array2 = -[0] * 10 // равнозначно -[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Why even need a static array and a fixed size array when there is a more versatile dynamic array?

In order not to dynamically allocate memory for a small array. This is especially useful in performance-critical areas of the code, as well as simply for the sake of convenience: for example, for the triangle task, it is convenient to use an array of fixed size, which consists of 3 elements that specify the coordinates of the triangle’s vertices.

It’s worth noting that static array in 11l is more advanced than std::array or boost::array in C++ (it’s essentially a TArray analog> Unreal Engine): A static array in 11l allows you to add and remove elements, and supports almost all methods (all but the reserve method) and operators applicable to normal dynamic arrays.

Conclusion

As you can see, there are very few really useful applications of loading based on argument names in my experience, and it would be great to see at least a few more and discuss the usefulness of this feature in the programming language you’re using.

[или необходимость добавления этой возможности, если ваш язык её ещё не поддерживает]

.

Related posts