В Perl 6 есть ключевое слово multi, которое создает так называемые мультифункции (multi-subs). Это ни что иное как множественная диспетчеризация (multiple dispatch), встроенная в язык.
В отличие от обычных функций, которые объявляются со словом sub, мультифункции требуют двух слов: multi sub. Имя функции при этом остается одним и тем же для всего набора мультифункций. В зависимости от выполненных условий, компилятор выбирает один из вариантов функции.
Число аргументов
Рассмотрим несколько примеров. Первый пример — вычисление расстояния от начала координат до точки на прямой, на плоскости или в трехмерном пространстве.
multi sub dist($x) {
return $x;
}
multi sub dist($x, $y) {
return sqrt($x ** 2 + $y ** 2);
}
multi sub dist($x, $y, $z) {
return sqrt($x ** 2 + $y ** 2 + $z ** 2);
}
say dist(2); # 2
say dist(3, 4); # 5
say dist(8, 9, 12); # 17
В этом примере функции различаются исключительно числом аргументов. При вызове, Perl 6 однозначно определяет, какой из вариантов следует вызвать.
Типы аргументов
Второй пример использует для выбора тип аргумента. Совсем не обязательно для этого создавать собственные классы, можно начать с типов, встроенных в язык.
multi sub mirror(Int $i) {
-$i
}
multi sub mirror(Str $s) {
$s.flip
}
say mirror(42); # -42
say mirror('42'); # 24
Здесь функция возвращает должна вернуть «зеркальный» вариант своего аргумента, чтобы это ни значило. Точный смысл вы в праве установить самостоятельно для каждого типа аргумента. Для целых чисел (Int $i) это будет противоположное число, а для строки (Str $s) — строка, записанная в обратном порядке.
Точно так же возможно определить свой класс и дополнить мультифункцию соответствующим вариантом. Во-первых, определим класс для светофора, у которого есть текущее значение value. Во-вторых, создадим мультифункцию, которая ожидает такой объект и инвертирует его:
class TrafficLight {
has Str $.value;
}
multi sub mirror(TrafficLight $obj) {
$obj.value eq 'red' ?? 'green' !! 'red'
}
say mirror(TrafficLight.new(value => 'green')); # red
Ограничение на значение аргументов
Еще один вариант — выбор одной из мультифункций, опираясь на значение аргументов. Для простоты рассмотрим простейший вариант функции с одним аргументом, которая для коротких слов печатает предупреждение о том, что пароль слишком короткий:
multi sub set-password($pwd) {
say "Password '$pwd' is OK"
}
multi sub set-password($pwd where {$pwd.chars <= 5}) {
say "Password '$pwd' is too short!"
}
set-password('He11oW0rld!'); # OK
set-password('qwert'); # too short!
Во втором варианте функции значение аргумента уточняется с помощью ключевого слова where:
multi sub set-password($pwd where {$pwd.chars <= 5}) { . . . }
Эта функция будет вызвана только в том случае, если выполняется условие $pwd.chars <= 5, то есть когда строка недостаточно длинна.
Обратите внимание, что для первого варианта функции никаких ограничений нет, тем не менее, Perl 6 принимает совершенно правильно решение, опираясь на весь комплект функций, и не активирует этот вариант для короткой строки.
На сегодня все. Завтра будем использовать мультифункции для создания рекурсии.