[1, 2, 3].inject(10) do |sum, number| sum += number end => 16or can have curly brackets:
[1, 2, 3].inject(10) { |sum, number| sum += number } => 16The only syntactic difference between both is the higher precedence of the {} (curly brackets) compared to do...end blocks. And as a side note therefore this is syntactically wrong:
[1, 2, 3].inject 10 { |sum, number| sum += number } # SyntaxError: compile errorwhile this one is correct:
[1, 2, 3].inject 10 do |sum, number| sum += number end => 16Besides that meaningless syntactic difference, using only one over the other misses the chance to write meaningful blocks.
Even using the convention to use {} (curly brackets) for one liners and do...end blocks for multi liners does not add any more sense to the code. Everyone can distinct between a one liner and multi liner without that convention. It is just meaningless noise.
But you can add sense by using {} (curly brackets) for functional blocks and do...end blocks for procedural blocks.
The primary purpose of functional blocks is to return a value:
[1, 42, 1024].map { |number| number.to_s.rjust(4, '0') } => ["0001", "0042", "1024"] Dir.glob('*.txt').inject('') { |text, file_name| file = File.open file_name, 'r' text << "#{file_name.chomp('.txt')}:#{file.read}" } => "en:Hello! fr:Salut! es:Hola!"whereas the primary purpose of procedural blocks is to change the state of the system in some way:
numbers = [1, 42, 1024] numbers.map! do |number| number.to_s.rjust(4, '0') end => ["0001", "0042", "1024"] Dir.glob('*.txt').each do |file_name| file = File.open file_name, 'w' file.write file_name.chomp('.txt') end => ["en.txt" , "fr.txt", "es.txt"]Both examples look like doing almost similar things. Especially the first example (Enumerable#map and Enumerable#map!) does the same task inside the block, but differs in the way it effects the "outer world". Enumerable#map! not only returns the result, but also changes the state of the numbers array itself.
The second example (Dir#glob) opens a bunch of files in both versions. But while the first one only reads from each file and returns a string, the second writes to each file. And that is exactly the point.
Some points, why this convention makes totally sense:
- No need to change the block style from {} to do...end or vice versa, when the number of lines change
- Visual cue about the intent of the code that wouldn’t otherwise be there (quick information if the block has side effects or not)
- Method chaining onto a functional block (curly brackets) is quite natural (read Chain your Ruby methods!)
A big salute goes to Avdi Grimm and Jim Weirich for bringing up this convention.
Supported by Ruby 2.1.1
Chain your Ruby methods!
Keine Kommentare:
Kommentar veröffentlichen