Недавно я влюбился в простой шаблон функции высшего порядка в JavaScript. Это выглядит примерно так:
Учитывая массив,
// .js const oneToTen = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
это:
oneToTen.map(number => number * 3) //=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ]
то же самое, что и это:
const timesThree = number => number * 3 oneToTen.map(timesThree) //=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ]
Этот пример корпоративного уровня не является сверхсложным, но вы можете себе представить, насколько он может быть полезен для удобочитаемости и разделения проблем по мере увеличения длины строки блоков. Тот же шаблон работает для других методов, таких как Array.prototype.reduce
и Array.prototype.filter
.
После некоторых путешествий в JavaScriptopolis и возвращения в Rubyland я обнаружил, что хочу использовать аналогичный шаблон. Поскольку методы не являются объектами первого класса в Ruby, это не так просто. Но я вспомнил свои ранние дни Ruby, когда наткнулся на идеи процедур и лямбда-выражений, и что они были связаны с выполнением блоков кода.
В то время я избегал их, поскольку тогда они казались слишком сложными, но на этот раз они казались более приемлемыми. Для контекста:
Для меня это означает, что объекты proc - это способ Ruby передавать функции как объекты. Лямбды - это особые виды процедур. Эти различия хорошо задокументированы здесь, но выходят за рамки этой статьи.
Интересный факт: proc - это сокращение от procedure.
Вот что я придумал, используя лямбды, для следования приведенному выше шаблону JavaScript:
Учитывая массив,
# .rb one_to_ten = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
это:
one_to_ten.map { |number| number * 3 } #=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ]
то же самое, что и это:
def times_three lambda do |number| number * 3 end end one_to_ten.map(×_three) #=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ]
который также совпадает с этим (синтаксис «stabby lambda»):
def times_three -> number { number * 3 } end one_to_ten.map(×_three) #=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ]
и лямбда также может быть сохранена в простой переменной ol ’:
times_three = -> number { number * 3 } one_to_ten.map(×_three) #=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ]
Прекрасный. Наша функция .map
вызывает лямбда в очень декларативном стиле, а лямбда заботится об императивной части.
А как насчет аргументов?
Представьте себе другой корпоративный случай, когда вам нужна функция, которая сообщает вам, все ли слова больше заданного числового аргумента:
words = ["apple", "okay", "not", "yes", "forgettable"] def longer_than(number) -> word { word.length > number } end words.all?(&longer_than(5)) #=> false words.all?(&longer_than(2)) #=> true
Поскольку у нас определена лямбда longer_than
, мы можем использовать ее для любого другого перечислимого:
words.select(&longer_than(3)) #=> ["apple", "okay", "forgettable"] words.reject(&longer_than(3)) #=> ["not", "yes"] words.one?(&longer_than(5)) #=> true
Еще раз times_three
:
Чтобы сделать нашу times_three
лямбду еще более дистиллированной и динамичной, мы можем сделать это:
def times(factor) -> number { number * factor } end one_to_ten.map(×(3)) #=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ] one_to_ten.map(×(5)) #=>[ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 ]
Еще лучше:
def operate(sign, arg) -> number { number.send(sign, arg) } end one_to_ten.map(&operate(:*, 3)) #=>[ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30 ] one_to_ten.map(&operate(:-, 6)) #=> [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 ]
Ура! Функции высшего порядка, которые принимают лямбда (или специальную процедуру) в качестве аргумента, и этот аргумент может принимать аргументы. Секундочку ...
… Хорошо, я вернулся. Мне пришлось победно вскинуть руки за то, что я написал такой прекрасно читаемый код.
Вывод
Учитывая эти примеры, я надеюсь, что вы сочтете лямбды такими же разумными, желательными и КРАСИВЫМИ, как и я. Вы можете деконструировать свои перечислимые вызовы, чтобы они были более декларативными, оставив лямбда для обработки фактической реализации. Так что в следующий раз, когда вы обнаружите, что набираете do |var|
или { |var| ...
вашего перечислимого типа, добавьте туда лямбду.