зробіть проти фігурних дужок для блоків у Ruby


154

У мене є колега, який активно намагається переконати мене в тому, що я не повинен використовувати do..end і замість цього використовувати фігурні дужки для визначення багаторядкових блоків у Ruby.

Я твердо в таборі, використовую лише фігурні брекети для коротких одноводкових лайнерів і робити .. закінчувати все інше. Але я думав, що я змушуся звернутися до більшої громади, щоб отримати певну резолюцію.

Так що це, і чому? (Приклад деякого коду musta)

context do
  setup { do_some_setup() }
  should "do somthing" do
    # some more code...
  end
end

або

context {
  setup { do_some_setup() }
  should("do somthing") {
    # some more code...
  }
}

Особисто я лише дивлячись на вищезазначене, відповідає мені на питання, але я хотів відкрити це для більшої громади.


3
Цікаво, але які аргументи ваших колег щодо використання брекетів? Здається, це більше як особиста перевага, ніж логічна річ.
Даніель Лі

6
Якщо ви хочете обговорення, зробіть це вікі спільноти. На ваше запитання: Це просто особистий стиль. Я віддаю перевагу фігурним брекетів, оскільки вони виглядають більш нерозумно.
полудан

7
Це не є перевагою, є синтаксичні причини використання однієї над іншою, крім стилістичних причин. Деякі відповіді пояснюють, чому. Якщо не використовувати правильного, це може спричинити дуже тонкі помилки, які важко знайти, якщо буде зроблений інший "стилістичний" вибір, щоб ніколи не використовувати обгорткові дужки для методів.
Олов'яний чоловік

1
"Крайні умови" мають погану звичку підкрадатися до людей, які не знають про них. Кодування захисно означає багато речей, у тому числі вирішити використовувати стилі кодування, які мінімізують шанс коли-небудь зіткнутися зі справами. Ви можете бути в курсі, але хлопець двох людей після вас, можливо, не буде після забуття племінних знань. На жаль, це відбувається, на жаль, у корпоративному середовищі.
Олов'яний чоловік

1
@tinman Ці типи крайових умов обробляються TDD. Ось чому Rubist може написати такий код і отримати повний відпочинок на ніч, знаючи, що в їх коді таких помилок не існує. При розробці з TDD або BDD і така помилка в перевазі робиться червоним, що відображається на екрані - це те, що нагадує нам про "племінних знаннях". Зазвичай рішення полягає в тому, щоб десь додати пару паронів, що цілком прийнятно в рамках стандартних умов. :)
Блейк Тейлор

Відповіді:


246

Загальна умова полягає у використанні do..end для багаторядкових блоків і фігурних дужок для однорядкових блоків, але існує також різниця між двома, що можна проілюструвати на цьому прикладі:

puts [1,2,3].map{ |k| k+1 }
2
3
4
=> nil
puts [1,2,3].map do |k| k+1; end
#<Enumerator:0x0000010a06d140>
=> nil

Це означає, що {} має вищий пріоритет, ніж до..закінчення, тому майте це на увазі, вирішуючи, що ви хочете використовувати.

PS: Ще один приклад, який потрібно пам’ятати, коли ви розвиваєте свої вподобання.

Наступний код:

task :rake => pre_rake_task do
  something
end

насправді означає:

task(:rake => pre_rake_task){ something }

І цей код:

task :rake => pre_rake_task {
  something
}

насправді означає:

task :rake => (pre_rake_task { something })

Отже, щоб отримати фактичне визначення, яке ви хочете, з фігурними дужками, ви повинні зробити:

task(:rake => pre_rake_task) {
  something
}

Можливо, використовувати дужки для параметрів - це те, що ви хочете зробити в будь-якому випадку, але якщо ви цього не зробите, то, мабуть, найкраще використовувати do..end в цих випадках, щоб уникнути цієї плутанини.


44
Я голосую за те, щоб завжди використовувати круглі дужки навколо параметрів методу, щоб пройти стороною цілого питання.
Олов'яний чоловік

@ The Tin Man: Ви навіть використовуєте дужки в Rake?
Ендрю Грімм

9
Це звичка для програмування старої школи. Якщо мова підтримує дужки, я використовую їх.
Олов'яний чоловік

8
+1 для вказівки на пріоритет. Я іноді отримую несподівані винятки, коли намагаюся конвертувати виконання; закінчення до {}
idrinkpabst

1
Чому puts [1,2,3].map do |k| k+1; endдрукується лише перелік, тобто одне. Тут не винесені два аргументи, а саме [1,2,3].mapіdo |k| k+1; end ?
ben

55

З програмування Ruby :

Брекети мають високий пріоритет; do має низький пріоритет. Якщо виклик методу має параметри, які не укладені в дужки, форма дужки блоку буде прив'язуватися до останнього параметра, а не до загального виклику. Форма do буде прив’язана до виклику.

Отже код

f param {do_something()}

Прив’язує блок до paramзмінної під час коду

f param do do_something() end

Прив’язує блок до функції f .

Однак це не проблема, якщо ви додаєте аргументи функції в дужки.


7
+1 "Однак це не проблема, якщо ви додаєте аргументи функції в дужки." Я роблю це за звичкою, а не покладаюся на випадковість та примху перекладача. :-)
Олов'яний чоловік

4
@theTinMan, якщо ваш перекладач використовує випадковість у своєму аналізаторі, вам потрібен новий перекладач.
Пол Дрейпер

@PaulDraper - дійсно, але не всі мови згодні з цим питанням. Але (я думаю) досить часто рідні парени не «виконують свою роботу», тому використання парен універсально позбавляє вас від необхідності пам’ятати про «випадкові» рішення, прийняті мовними дизайнерами - навіть якщо вони сумлінно виконуються виконавцями укладачів та перекладачів .
dlu

15

На це є кілька точок зору, це справді питання особистої переваги. Багато рубінів застосовують ваш підхід. Однак два спільних стилю - це завжди використовувати той чи інший або використовувати {}для блоків, що повертають значення, і do ... endдля блоків, які виконуються для побічних ефектів.


2
Справа не лише в особистих уподобаннях. Прив’язка параметрів може спричинити непомітні помилки, якщо параметри методу не укладені в дужки.
Олов'яний чоловік

1
Наразі я не роблю останнього варіанту, але я поставляю +1 за те, щоб згадати, що деякі люди дотримуються такого підходу.
Ендрю Грімм

Авді Грімм виступає за це у своєму дописі у блозі: devblog.avdi.org/2011/07/26/…
alxndr

8

Є одна головна перевага фігурних брекетів - у багатьох редакторів набагато легший час їх узгодження, що значно полегшує певні типи налагодження. Тим часом, ключове слово "зробити ... закінчити" порівняно трохи складніше, тим більше, що "кінець" також збігається з "якщо".


Наприклад, do ... end
RubyMine

1
Хоча я віддаю перевагу дужки, я не бачу причин, щоб у редактора виникли проблеми з пошуку 2/3 символьних рядків проти одного символу. Хоча є аргумент, що більшість редакторів вже відповідають дугам, тоді do ... endяк це особливий випадок, який вимагає конкретної мови.
Chinoto Vokro


7

Я голосую за "до / кінець"


Конвенція призначена do .. endдля багатолінійних та { ... }однолінійних.

Але мені подобається do .. endкраще, тому коли у мене є один вкладиш, я do .. endвсе-таки використовую, але відформатую його як завжди для do / end у три рядки. Це робить усіх щасливими.

  10.times do 
    puts ...
  end

Одна з проблем { }полягає в тому, що він є віршованим для режиму поезії (оскільки вони щільно прив'язуються до останнього параметра, а не до всього виклику методу, тому ви повинні включити паролі методів), і вони, на мій погляд, не виглядають так приємно. Вони не є групами заяв, і вони стикаються з хеш-константами для читабельності.

Плюс, я бачив досить { }у програмах на С. Шлях Рубі, як завжди, краще. Існує рівно один тип ifблоку, і вам ніколи не доведеться повертатися назад і перетворювати оператор у складний-оператор.


2
"Я бачив достатньо {} в програмах на С" ... і Perl. І +1 для декількох заяв, що там є, і за відстоювання стилів кодування Рубі в дзен. :-)
The Tin Man

1
Власне кажучи, існує ще один синтаксис "if" - постфікс "if" ( do_stuff if x==1), який наче дратує перетворення в звичайний синтаксис. Але це ще одна дискусія.
Кельвін

Є префікс if, постфікс if, постфікс unless(також різновид if, якщо ні) та один рядок ifз then. Шлях Рубі - це мати безліч способів висловити насправді щось, що завжди одне і те ж, набагато гірше, ніж те, що пропонує С, що дотримується дуже простих, строгих правил.
Мецькі

2
Отже, якщо нам подобаються {} в C, це означає, що ми повинні їх використовувати і в Ruby?
Warren Dew

6

Кілька впливових рубістів пропонують використовувати дужки, коли ви використовуєте значення повернення, і виконувати / закінчувати, коли цього не робите.

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace (на archive.org)

http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (на archive.org)

Це здається гарною практикою загалом.

Я б трохи змінив цей принцип, щоб сказати, що вам слід уникати використання do / end в одному рядку, оскільки це важче читати.

Вам потрібно бути обережнішими, використовуючи дужки, тому що вони будуть прив’язані до остаточної параметри методу замість всього виклику методу. Просто додайте дужки, щоб уникнути цього.


2

Насправді це особисті переваги, але сказавши, що за останні 3 роки мого досвіду рубіну я дізнався, що рубін має свій стиль.

Одним із прикладів є, якщо ви переходите з фону JAVA, для булевого методу, який ви можете використовувати

def isExpired
  #some code
end 

зверніть увагу на випадок верблюда, і найчастіше це "префікс", щоб визначити його як булевий метод.

Але в рубіновому світі такий же метод був би

def expired?
  #code
end

тому я особисто думаю, що краще піти «рубіновим шляхом» (але я знаю, що потрібно певний час, щоб хтось зрозумів (на це пішло близько 1 року: D)).

Нарешті, я б пішов з

do 
  #code
end

блоки.


2

Я поставив ще один відповідь, хоча велика різниця вже була вказана (prcedence / прив'язка), і це може спричинити важкі проблеми (Чоловік з жерсті та інші вказали на це). Я думаю, що мій приклад показує проблему з не таким звичним фрагментом коду, навіть досвідчені програми не читають, як у неділю:

module I18n
    extend Module.new {
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    }
end

module InplaceTrans
    extend Module.new {
        def translate(old_translate, *args)
            Translator.new.translate(old_translate, *args)
        end
    }
end

Тоді я зробив кілька прикрашаючих код ...

#this code is wrong!
#just made it 'better looking'
module I18n
    extend Module.new do
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end
end

якщо ви зміните {}тут, do/endви отримаєте помилку, цього методу translateне існує ...

Чому це трапляється, тут вказується більше, ніж один. Але куди тут поставити брекети? (@ The Tin Man: Я завжди використовую брекети, як ти, але тут ... під наглядом)

тому кожна відповідь, як

If it's a multi-line block, use do/end
If it's a single line block, use {}

просто неправильно, якщо використовується без "АБО слідкуйте за дужками / пріоритетом!"

знову:

extend Module.new {} evolves to extend(Module.new {})

і

extend Module.new do/end evolves to extend(Module.new) do/end

(що коли-небудь результат розширення робить з блоком ...)

Отже, якщо ви хочете використовувати do / end використання цього:

#this code is ok!
#just made it 'better looking'?
module I18n
    extend(Module.new do 
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end)
end

1

Зводиться до особистих упереджень, я віддаю перевагу фігурним дужкам над блоком do / end, оскільки це зрозуміліше для більшої кількості розробників через те, що більшість фонових мов використовують їх над умовами do / end. Зважаючи на це, справжнім ключем є домовленість у вашому магазині, якщо розробники 6/10 використовують 6/10 розробників, ніж КОЖНІ повинні використовувати їх, якщо 6/10 використовують фігурні дужки, то дотримуйтесь цієї парадигми.

Вся справа в створенні шаблону, щоб команда в цілому могла швидше визначити структури коду.


3
Це більше, ніж перевага. Зв'язок між ними різний і може спричинити проблеми, які важко діагностувати. Кілька відповідей детально описують проблему.
Олов'яний чоловік

4
Що стосується людей іншого мовного походження - я вважаю, що різниця в «зрозумілості» в цьому випадку настільки мала, що не вимагає розгляду. (Це був не мій нижній пробіг.)
Келвін

1

Між ними існує тонка різниця, але {} прив'язується більш жорстко, ніж робити / закінчувати.


0

Мій особистий стиль полягає в тому, щоб підкреслити читабельність за жорсткими правилами {... }проти do... endвибору, коли такий вибір можливий. Моя ідея читабельності така:

[ 1, 2, 3 ].map { |e| e + 1 }      # preferred
[ 1, 2, 3 ].map do |e| e + 1 end   # acceptable

[ 1, 2, 3 ].each_with_object [] do |e, o| o << e + 1 end # preferred, reads like a sentence
[ 1, 2, 3 ].each_with_object( [] ) { |e, o| o << e + 1 } # parens make it less readable

Foo = Module.new do     # preferred for a multiline block, other things being equal
  include Comparable
end

Foo = Module.new {      # less preferred
  include Comparable
}

Foo = Module.new { include Comparable }      # preferred for a oneliner
Foo = module.new do include Comparable end   # imo less readable for a oneliner

[ [ 1 ], [ 1, 2 ] ].map { |e| e.map do |e| e + 1 end }  # slightly better
[ [ 1 ], [ 1, 2 ] ].map { |e| e.map { |e| e + 1 } }     # slightly worse

У більш складних синтаксисах, таких як багаторядкові вкладені блоки, я намагаюся перехрестити {... }і do... endроздільники для більшості природних результатів, наприклад.

Foo = Module.new { 
  if true then
    Bar = Module.new {                          # I intersperse {} and keyword delimiters
      def quux
        "quux".tap do |string|                  # I choose not to intersperse here, because
          puts "(#{string.size} characters)"    # for multiline tap, do ... end in this
        end                                     # case still loks more readable to me.
      end
    }
  end
}

Хоча відсутність жорстких правил може призвести до різко різного вибору для різних програмістів, я вважаю, що оптимізація для читання в кожному конкретному випадку, хоча і суб'єктивна, є чистим виграшем від дотримання жорстких правил.


1
Походячи з Python, можливо, я повинен пропустити Рубі і навчитися Wisp замість цього.
Cees Timmerman

Я вважав, що Rubyце найкраща прагматична мова там. Чи варто сказати сценарій мови. Сьогодні я думаю, ви б однаково добре навчалися Brainfuck.
Boris Stitnicky

0

Я взяв приклад найбільш голосової відповіді тут. Сказати,

[1,2,3].map do 
  something 
end

Якщо ви перевірите, що таке внутрішня реалізація в array.rb, заголовок методу map говорить :

Invokes the given block once for each element of self.

def map(*several_variants)
    (yield to_enum.next).to_enum.to_a
end

тобто він приймає блок коду - все, що знаходиться між " до" і " кінцем" , виконується як вихід. Тоді результат збирається знову як масив, таким чином, він повертає абсолютно новий об'єкт.

Тому щоразу, коли ви стикаєтеся з блоком завершення або {} , просто майте на увазі, що блок коду передається як параметр, який буде виконуватися внутрішньо.


-3

Є третій варіант: Написати препроцесора, щоб зробити висновок "кінця" на його власному рядку з відступу. Глибокі мислителі, які вважають за краще стислий код, трапляються правильно.

А ще краще, рубати рубін, так що це прапор.

Звичайно, найпростішим рішенням "вибрати свої бійки" є прийняття конвенції стилів, щоб послідовність кінців відображалася на одній лінії, і навчити розмальовки синтаксису їх приглушувати. Для зручності редагування можна використовувати сценарії редактора для розширення / згортання цих послідовностей.

Від 20% до 25% моїх рубінових ліній коду "закінчуються" на їхньому власному рядку, про все тривіально випливає з моїх відступів. Рубі - це мова, що нагадує ліс, що досягла найбільшого успіху. Коли люди оскаржують це, запитуючи, де всі ганебні дужки, я показую їм функцію рубіну, що закінчується на сім рядків зайвого "кінця".

Я роблю код протягом багатьох років, використовуючи препроцесор lisp для виведення більшості дужок: Рядок '|' відкрив групу, яка автоматично закрилася в кінці рядка, і знак долара "$" слугував порожнім заповнювачем місця, де в іншому випадку не було б символів, які могли б зробити висновок про групування. Звичайно, це територія релігійної війни. Ліпс / схема без дужок є найбільш поетичною з усіх мов. Тим не менш, простіше просто заспокоїти дужки, використовуючи синтаксичне забарвлення.

Я все ще кодую з препроцесором Haskell, щоб додати гередоки та за замовчуванням всі флеш-рядки як коментарі, все з відступом як код. Мені не подобаються персонажі коментарів, незалежно від мови.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.