Если я правильно понимаю ваш вопрос, вы хотите дважды применить функцию к аргументу и проверить ее тоже.
Например, если вам нужно применить Math.sqrt
дважды к аргументу, вы можете достичь его, как показано в следующем коде:
val sqrt: Double => Double = Math.sqrt
def applyTwice[A](f: A => A, d: A) = {
f(f(d))
}
println(applyTwice[Double](sqrt, 625))
assert(applyTwice[Double](sqrt, 625) == 5.0) // will check if applyTwice return 5.0
После предоставления прямого ответа на Ваш вопрос я хотел бы также оспаривать предпосылку; каждый раз, когда группа программистов характеризует пользователей другого языка таким образом, разногласия - то, что они говорят Вам больше о себе, чем о сообществе, которое они описывают.
Вы могли, например, обвинить c программистов в том, что они были слишком одержимым низкоуровневыми деталями или haskell программистов с тем, чтобы быть ослепленным их требованием функциональной чистоты; торговцы жемчуга для краткости, и т.д. Но Вы были бы, по моему скромному мнению, путем получения причинной связи назад, когда Вы делаете так.
Когда я хочу записать программу, которая лучше всего выражается в определенном стиле, я пытаюсь выбрать язык, который поддерживает тот стиль. Иногда Вы хотите инструмент, который позволяет Вам сделать, необычные вещи, и для такой задачи, имеющей язык такой столь же рубиновый, так же ценны как имеющий mathematica для математики или JavaScript для управления браузером в Вашем инструментарии. Если я хочу играть с типографией, я скачкообразно двигаюсь в постскриптум, потому что это - то, в чем это является лучшим.
Это похоже на высказывание, "Вы когда-либо замечали, что люди, которые используют электродрели, всегда вводят дыры по абсолютному адресу в вещах?" Это верно, но это вид упускает суть.
class Tree
def initialize*d;@d,=d;end
def to_s;@l||@r?"<#{@d},<#{@l}>,<#{@r}>>":@d;end
def total;(@d.is_a?(Numeric)?@d:0)+(@l?@l.total: 0)+(@r?@r.total: 0);end
def insert d
alias g instance_variable_get
p=lambda{|s,o|d.to_s.send(o,@d.to_s)&&
(g(s).nil??instance_variable_set(s,Tree.new(d)):g(s).insert(d))}
@d?p[:@l,:<]||p[:@r,:>]:@d=d
end
end
Двойной удар:!! что-то
Я не собираюсь писать то, что это делает. Забудьте, что Вы когда-либо видели этот синтаксис.
Любое использование метапрограммирования не думая чертовски трудный о том, существует ли лучший способ достигнуть этого использования нормального, non-'meta' идиомы языка, я склонен находить раздражающим.
Одержимость "DRY" (не повторяют себя), где некоторый жестокий кусок спагетти метапрограммирования вызывается, чтобы не повторять себя, скажем, дважды простым и actually-more-straightforward-and-readable-than-the-alternative способом.
Любое использование 'оценки' в частности. Когда метапрограммирование идет, этот должен быть Вашим абсолютным последним средством после попытки всего остального. например, много rubyists, кажется, не услышало о Class#define_method.
Выходная фаза yaml.rb; вот почему я создал в соавторстве zaml.rb. Стандарт yaml версия делает все виды метапрограммирования (это было первоначально записано why-the-lucky-stiff, кем я обычно восхищаюсь), но путем замены его прямой иерархической версией, которая непосредственно отображается на дерево класса, которое мы смогли устранить несколько O (n^3) случаи, приводящие к фактору десяти ускорений для случаев интереса, исправить несколько ошибок и сделать так в части кода.
Плюс, даже люди, которые не являются рубиновыми гуру, видят то, что это делает.
Многие примеры в этой статье, казалось бы, квалифицировали бы:
21 прием Ruby необходимо Использовать в собственном коде.
Заголовок статьи был чем-то вроде дешевой распродажи, учитывая, что это читает, "Должен" вместо, "не Должен". Код "должен" быть прозрачным. Код "не должен" быть хитрым.
Я не уверен, квалифицирует ли это как "слишком умное", но я видел код, который заставил меня задаться вопросом, был ли автор или гением или идиотом. У одного разработчика, казалось, было правило, что никакой метод не должен иметь больше чем две строки кода. Это продвинуло стек вызовов очень глубоко и сделало отладку довольно трудного. Позитивный аспект - то, что его общий замысел был очень абстрактен и даже изящен издалека.
Огурец (или истории RSpec)
Заключенный в кавычки из вышеупомянутого ссылка Историй RSpec:
Базирующийся вокруг описаний простого текста поведения приложения, это позволяет Вам записать интеграционные тесты с хорошим повторным использованием и хорошим диагностическим созданием отчетов.
Например, вот история, которую я записал для проверки процесса входа в систему.
Story: login as an existing user As an unauthenticated user I want to log in to Expectnation So I can see my account details Scenario: login details are correct Given an event provider And my test@example.org account When I log in with email test@example.org and password foofoo Then I will be logged in And I will be shown the account page
Слова такой, как "Дали", "Когда" и "Затем" сигналы бегуну истории для выполнения некоторого кода. Позади истории находится набор шагов. Вот несколько шагов от этого теста:
Given "my $email account" do |email| @user = find_or_create_user_by_email({:email => email, :password => 'foofoo', :password_confirmation => 'foofoo'}) end When "I log in with email $email and password $password" do |email, password| post '/user/account/authenticate', :user => {:email => email, :password => password} end
Заметьте, как умный бит сопоставления строк позволяет Вам передавать параметры от прозы истории.
С маленьким битом соединения болтом вместе, истории прозы затем выполняются как код и выполняемые тесты.
method_missing можно злоупотребить, и это - одна из тех вещей, которые могут заставить Вас вытаскивать волосы, когда необходимо исправить ошибку спустя 3 месяца после написания кода.
Это зависит. (Я люблю, "это зависит" вопросы),
Это зависит от знания устройства записи и средства чтения. Я раньше думал, что использование Symbol#to_proc в направляющих было излишне тайным, например, предпочтя
a.map { |e| e.downcase }
кому:
a.map(&:downcase)
Теперь я счастлив, когда я считал его, хотя я все еще не думаю для записи этого.
Существуют области библиотек (Направляющие и другие), где я чувствовал себя чрезмерным, и потакающее своим желаниям метапрограммирование, возможно, произошло, но снова подразделение между "слишком умным" и "действительно очень умный действительно" часто тонко как бумага. DSLs являются хорошим местом: пути, которыми "макросами" сделаны доступными в классах (думают обо всем этом декларативном совершенстве в вещах как ActiveRecord:: Основа или ActionController:: Основа), очень трудно для относительного новичка понять и вероятно походил бы на сверхум. Это сделало мне. Теперь я ссылаюсь на тот же код для руководства, как я реализую подобные возможности.
Вам не придется пойти от метода до метода к методу, чтобы попытаться выяснить то, что в аду что-то делает для единственной цели не повторения нескольких строк кода. Быть слишком сфокусированным на количестве LOC и ультратощих методах могло бы чувствовать себя прохладным в то время, но является трудоемким для кого-то еще пытающегося отлаживать или следовать коду (и что кто-то может быть Вами несколько месяцев спустя).
сравните:
if MODELS.keys.inject(true) {|b, klass| b and klass.constantize.columns.map(&:name).include? association.options [:foreign_key]} then
# ...
end
1 строка (если), 132 символа, 132 в среднем len, 22.9 flog
по сравнению с
fk = association.options[:foreign_key]
columns = MODELS.keys.map { |key| key.constantize.columns.map { |c| c.name } }
if columns.all? {|column| column.include? fk} then
# ...
end
4 строки, 172 символа, 43 в среднем символы, 15.9 flog
намного быстрее также.
Исходный автор на самом деле обсудил пригодность для обслуживания для первой версии.
Недавно обнаружил этого монстра:
def id
unless defined?(@id)
@id = if id = local_body.to_s[/(?:#\s*|@[[:punct:]]?)#{URL_REGEX}/,1]
id.to_i
end
end
@id
end
Не то чтобы я не согласен с кешированием вычислений, это могло бы быть гораздо более ясным.