Почему 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 вместо блока на самом деле вообще не похож, поскольку в этом случае он указывает на то, что будет вызываться на элементах перечислимого.