Почему Enumerable # detect нуждается в Proc/lambda?
Enumerable#detect
возвращает первое значение массива, где блок оценивает значение true
. Он имеет необязательный аргумент, который должен отвечать на call
и в этом случае вызывается, возвращая его значение. Таким образом,
(1..10).detect(lambda{ "none" }){|i| i == 11} #=> "none"
Зачем нам нужна лямбда? Почему бы нам просто не передать значение по умолчанию, поскольку (в моих тестах) лямбда не может иметь никаких параметров? Вот так:
(1..10).detect("none"){|i| i == 11} #=> "none"
Ответы
Ответ 1
Как и все вещи в Ruby, применяется "принцип наименьшего удивления". Конечно, это не означает "наименьший сюрприз для вас". Мац совершенно откровенен в отношении того, что это на самом деле означает:
Каждый человек имеет индивидуальный фон. Кто-то может приехать из Python, кто-то может приехать из Perl, и они могут быть удивлены различными аспектами языка. Затем они подходят ко мне и говорят: "Я был удивлен этой особенностью языка, поэтому Руби нарушает принцип наименьшего удивления". Подождите. Подождите. Принцип наименьшего удивления - не только для вас. Принцип наименьшего удивления означает принцип наименьшего моего удивления. И это означает принцип наименьшего удивления после того, как вы хорошо изучите Ruby. Например, я был программистом на С++, прежде чем начал разрабатывать Ruby. Я программировал на С++ исключительно два или три года. И после двух лет программирования на С++ это все еще меня удивляет.
Итак, рациональное здесь действительно есть догадка.
Одна из возможностей заключается в том, что она допускает или согласуется с прецедентами, когда вы хотите условно запустить что-то дорогое:
arr.detect(lambda { do_something_expensive }) { |i| is_i_ok? i }
Или как намечено @majioa, возможно, чтобы передать метод:
arr.detect(method(:some_method)) { |i| is_i_ok? i }
Ответ 2
Принятие вызываемого объекта позволяет использовать "ленивые" и общие решения, например, в тех случаях, когда вы хотите сделать что-то дорогое, создать исключение и т.д.
Я не вижу причины, по которой detect
не мог принимать аргументы без вызова, хотя, особенно сейчас, в Ruby 2.1, где легко создать дешево замороженные литеры. Я открыл функцию для этого.
Ответ 3
Вероятно, вы можете создать подходящий результат из ввода. Затем вы можете сделать что-то вроде
arr = (1..10).to_a
arr.detect(lambda{ arr.length }){|i| i == 11} #=> 10
Как вы сказали, возвращение постоянной ценности очень просто с лямбдой.
Ответ 4
Действительно, это интересный вопрос. Я могу понять, почему авторы добавили эту функцию с вызовом метода, вы можете просто передать переменную method
, содержащую объект method
или аналогичный, в качестве аргумента. Я думаю, что это просто волюнтарическое решение для метода :detect
, потому что было бы легко добавить тип переключения передаваемого аргумента, чтобы выбрать weither, это method
или нет.
Я повторил примеры и получил:
(1..10).detect(proc {'wqw'}) { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
# => "wqw"
(1..10).detect('wqw') { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
# NoMethodError: undefined method `call' for "wqw":String
Это потрясающе. =)
Ответ 5
Наилучшим вариантом использования lambda является повышение пользовательского исключения
arr = (1..10).to_a
arr.detect(lambda{ raise "not found" }){|i| i == 11} #=> RuntimeError: not found
Итак, поскольку K тривиально (просто окружает с ->{ }
), нет никакого смысла в проверке на резервное поведение.
Подобный случай передачи символа & -ed вместо блока на самом деле вообще не похож, поскольку в этом случае он указывает на то, что будет вызываться на элементах перечислимого.