Ruby curly braces vs. do-end method chaining

回眸只為那壹抹淺笑 提交于 2019-12-25 02:41:20

问题


I have found a question: Nested Loops Ruby and solved it, but the solution looks ugly (although works):

puts (1..10).map { |i| (i..10).step(i).inject(0) { |memo, obj| memo = memo | 2**(obj-1) } }.inject(0) { |memo, obj| memo = memo ^ obj}

Rewritten to multiline, but keeping curly braces:

puts (1..10).map { |i| 
    (i..10).step(i).inject(0) { |memo, obj| 
        memo = memo | 2**(obj-1)
        }
    }.inject { |memo, obj|
        memo = memo ^ obj
        }

I tried to rewrite it into multiline do-end blocks to make it more readable (knowing about the precedence difference between {} and do-end), but I get an error (I just changed the last braces):

puts (1..10).map { |i| 
    (i..10).step(i).inject(0) { |memo, obj| 
        memo = memo | 2**(obj-1)
        }
    }.inject do |memo, obj|
        memo = memo ^ obj
        end.to_s(2)
../../bitflipping.rb:5:in 'each': no block given (LocalJumpError)
    from ../../bitflipping.rb:5:in 'inject'
    from ../../bitflipping.rb:5:in ''

Is it possible to rewrite this with do-end? I think there is a precedence problem, how can I regroup them so for example inject at the end gets the block properly?


回答1:


try making it into a method, maybe? although as sawa says, map{|x| x} doesn't do anything

def my_method 
  first_step = (1..10).map do |i| 
    (i..10).step(i).map { |x| x}.inject(0) { |memo, obj| memo = memo | 2**(obj-1) }
  end
  second_step = first_step.inject { |memo, obj| memo = memo ^ obj}.to_s(2)
  return second_step
end

puts my_method



回答2:


The syntactical construct that triggers the problem is the puts without parentheses.

You can fix the problem by refactoring the code so you're assigning the result to a variable (e.g. result) first, then doing puts result afterward.

Alternate solution is to wrap the entire expression in parentheses.

Here's a slimmed down reproduction of the scenarios:

# OK, because assignment has lower precedence than do/end 
result = (1..10).inject do |memo, obj|
  memo + obj
end
puts result

# OK because the outer parentheses resolves the ambiguity
puts(
  (1..10).inject do |memo, obj|
    memo + obj
  end
)

# ERROR: no block given
puts (1..10).inject do |memo, obj|
  memo + obj
end

The error happens because a do/end block has lower precedence than a method call (without parentheses).

The ERROR case is equivalent to:

puts( (1..10).inject ) do |memo, obj|
  memo + obj
end

... in other words, you're passing the block to puts instead of inject. And inject (at least in this case) fails because it requires a block.



来源:https://stackoverflow.com/questions/20424778/ruby-curly-braces-vs-do-end-method-chaining

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!