简体   繁体   中英

Ruby method call using round brackets throwing syntax error

I'm having some trouble understanding why I can't call select with round brackets.

a = [1,2,3]
a.select {|v| v.is_a?(Integer)}  # works
a.select({|v| v.is_a?(Integer)}) # syntax error, unexpected '|', expecting '}

As far as I can tell select is a regular method.

Array.method_defined? :select # true
Array.method_defined? :is_a?  # true

I though round brackets are optional for methods in ruby.
In the case below round brackets make no difference.

a.reverse() == a.reverse #true

I'm using ruby 2.2.1.
Any ideas?

You cannot pass a block with such a synatx, you would have to do something like this:

a.select(&lambda { |v| v.is_a?(Integer) })

but normally you would just do

a.select { |v| v.is_a?(Integer) }

which is the same as

a.select() { |v| v.is_a?(Integer) }

ie the block is outside the method parameters.

You could also use the 'stabby' lambda syntax:

is_a_integer = ->(v) { v.is_a?(Integer) }
a.select(&is_a_integer)

So if you want to pass a block as an argument you need to prefix with & , but usually you would have the block outside the parameters for atheistic reasons.

Also notice the difference between these to method signatures and the way they are called:

def call(&block)
  yield
end

call { 1 } # => 1

call(lambda { 1 }) # => ArgumentError

call(&lambda { 1 }) # => 1

and

def call(block)
  block.call
end

call { 1 } # => ArgumentError

call(lambda { 1 }) # => 1

call(&lambda { 1 }) # => ArgumentError

This is because lambda (and Procs) are objects hence we can do #call to evaluate them, but blocks are not and can be evaluated using the yield keyword. There is more information in this blog post .

lambda { 1 }.class # => Proc

Round brackets are a way to pass arguments to a method while the squiggly brackets (or do/end) are a way to pass a block to a method. They are not interchangeable.

Squiggly brackets can also be used to create a hash in ruby, which can cause some confusion.

Some ruby methods can take arguments and a block in which case you can use round brackets before the squiggly brackets: EG

open("ChangeLog") { |f|
    f.slice_before(/\A\S/).each { |e| pp e }
}

With or without parenthesis, Array#select doesn't accept any regular arguments. It, however accepts a block argument but the blocks associated with a method call are always placed after the parenthesis, not inside them.

Consequently, the call:

a.select {|v| v.is_a? Integer }

can also be written with parenthesis as:

a.select() {|v| v.is_a? Integer }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM