IT story

루비에서 블록에 대한 do..end vs 중괄호

hot-time 2020. 6. 24. 07:29
반응형

루비에서 블록에 대한 do..end vs 중괄호


나는 do..end를 사용해서는 안된다는 것을 적극적으로 확신시키는 동료가 있습니다. 루비에서 여러 줄 블록을 정의하기 위해 중괄호를 대신 사용하십시오.

나는 짧은 원 라이너에 중괄호만을 사용하고 다른 모든 것을 끝내기 위해 단단히 묶는 캠프에 단단히 있습니다. 그러나 나는 결의안을 얻기 위해 더 큰 공동체에 손을 뻗을 것이라고 생각했다.

그래서 그것은 무엇이며 왜 그렇습니까? (일부 머스트 코드의 예)

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...
  }
}

개인적으로 위의 내용을 살펴보면 나에게 질문에 대한 답이되지만 더 큰 커뮤니티에 공개하고 싶었습니다.


일반적인 규칙은 여러 줄 블록에 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

이것은 {}이 do..end보다 우선 순위가 높으므로 사용하려는 것을 결정할 때 명심하십시오.

추신 : 선호도를 개발하는 동안 명심해야 할 또 하나의 예입니다.

다음 코드 :

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를 사용하는 것이 가장 좋습니다.


에서 루비 프로그래밍 :

중괄호는 우선 순위가 높습니다. 우선 순위가 낮습니다. 메소드 호출에 괄호로 묶이지 않은 매개 변수가있는 경우 블록의 중괄호 양식은 전체 호출이 아닌 마지막 매개 변수에 바인드됩니다. do 양식은 호출에 바인딩됩니다.

그래서 코드

f param {do_something()}

param코드를 작성하는 동안 블록을 변수에 바인딩

f param do do_something() end

블록을 함수에 바인딩합니다 f.

그러나 함수 인수를 괄호로 묶는 경우 문제가되지 않습니다.


이것에 대한 몇 가지 견해가 있습니다. 그것은 실제로 개인적인 취향의 문제입니다. 많은 루비 스트들이 당신에게 접근합니다. 그러나 일반적인 두 가지 스타일은 항상 하나 또는 다른 스타일을 사용하거나 {}값을 반환 do ... end하는 블록 부작용을 위해 실행되는 블록에 사용하는 것입니다.


중괄호에는 한 가지 주요 이점이 있습니다. 많은 편집자가 편집하기가 훨씬 쉬워 특정 유형의 디버깅이 훨씬 쉬워졌습니다. 한편, "do ... end"라는 키워드는 일치시키기가 매우 어렵습니다. 특히 "end"도 "if"와 일치하기 때문입니다.


내가 본 가장 일반적인 규칙 (가장 최근에는 Eloquent Ruby에서 )은 다음과 같습니다.

  • 여러 줄 블록이면 do / end를 사용하십시오.
  • 단일 행 블록 인 경우 {}를 사용하십시오.

할 일 / 종료 투표


컨벤션은 여러 줄 do .. end{ ... }한 줄짜리를위한 것입니다.

그러나 나는 do .. end더 낫습니다. 따라서 하나의 라이너가 있으면 do .. end어쨌든 사용 하지만 세 줄로 do / end에 평소대로 형식을 지정하십시오. 이것은 모든 사람을 행복하게 만듭니다.

  10.times do 
    puts ...
  end

한 가지 문제 { }는 시가 모드 적대적이라는 것입니다 (전체 메소드 호출이 아니라 마지막 매개 변수에 단단히 바인딩되므로 메소드 구문을 포함해야합니다). 그냥 내 마음에 좋지 않습니다. 그것들은 문장 그룹이 아니며 가독성을 위해 해시 상수와 충돌합니다.

Plus, I've seen enough of { } in C programs. Ruby's way, as usual, is better. There is exactly one type of if block, and you never have to go back and convert a statement into a compound-statement.


A couple influential rubyists suggest to use braces when you use the return value, and do/end when you don't.

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

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

This seems like a good practice in general.

I'd modify this principle a bit to say that you should avoid using do/end on a single line because it's harder to read.

You do have to be more careful using braces because it'll bind to a method's final param instead of the whole method call. Just add parentheses to avoid that.


Comes down to personal bias, I prefer curly braces over a do/end block as its more understandable to a higher number of developers due to a majority of background languages use them over the do/end convention. With that being said the real key is to come to an agreement within your shop, if do/end is used by 6/10 developers than EVERYONE should be using them, if 6/10 use curly braces, then stick to that paradigm.

Its all about making a pattern so that the team as a whole can identify the code structures quicker.


There is a subtle difference between them, but { } binds tighter than do/end.


Actually it's a personal preference, but having said that, for past 3 years of my ruby experiences what I have learnt is that ruby has its style.

One example would be, if you are comming from a JAVA background , for a boolean method you might use

def isExpired
  #some code
end 

notice the camel case and most often 'is' prefix to identify it as a boolean method.

But in ruby world, the same method would be

def expired?
  #code
end

so I personally think, it's better to go with 'ruby way' (But I know it takes some time for one to understand (it took me around 1 year :D)).

Finally, I would go with

do 
  #code
end

blocks.


I put another Answer, although the big difference was already pointed out (prcedence/binding), and that can cause hard to find problems (the Tin Man, and others pointed that out). I think my example shows the problem with a not so usual code snippet, even experienced programmeres do not read like the sunday times:

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

Then i did some code beautifying ...

#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

if you change the {} here to do/end you will get the error, that method translate does not exist ...

Why this happens is pointed out here more than one - precedence. But where to put braces here? (@the Tin Man: I always use braces, like you, but here ... overseen)

so every answer like

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

is just wrong if used without the "BUT Keep an eye on braces / precedence!"

again:

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

and

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

(what ever the result of extend does with the block ...)

So if you want to use do/end use this:

#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

My personal style is to emphasize readability over rigid rules of {...} vs do...end choice, when such choice is possible. My idea of readability is as follows:

[ 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

In more complex syntax, such as multiline nested blocks, I try to intersperse {...} and do...end delimiters for most natural result, eg.

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
}

Although the lack of rigid rules might produce sligtly different choices for different programmers, I believe that case-by-case optimization for readability, although subjective, is a net gain over adherence to rigid rules.


There's a third option: Write a preprocessor to infer "end" on its own line, from indentation. The deep thinkers who prefer concise code happen to be right.

Better yet, hack ruby so this is a flag.

Of course, the "pick your fights" simplest solution is to adopt the style convention that a sequence of ends all appear on the same line, and teach your syntax coloring to mute them. For ease of editing, one could use editor scripts to expand/collapse these sequences.

20% to 25% of my ruby code lines are "end" on their own line, all trivially inferred by my indentation conventions. Ruby is the lisp-like language to have achieved the greatest success. When people dispute this, asking where are all the ghastly parentheses, I show them a ruby function ending in seven lines of superfluous "end".

I did code for years using a lisp preprocessor to infer most parentheses: A bar '|' opened a group that autoclosed at the end of the line, and a dollar sign '$' served as an empty placeholder where there would otherwise be no symbols to help infer the groupings. This is of course religious war territory. Lisp/scheme without the parentheses is the most poetic of all languages. Still, it is easier to simply quiet down parentheses using syntax coloring.

I still code with a preprocessor for Haskell, to add heredocs, and to default all flush lines as comments, everything indented as code. I dislike comment characters, whatever the language.

참고URL : https://stackoverflow.com/questions/5587264/do-end-vs-curly-braces-for-blocks-in-ruby

반응형