Marking an unused block variable

2019-03-13 06:23发布

问题:

When there is a block or local variable that is not to be used, sometimes people mark it with *, and sometimes with _.

{[1, 2] => 3, [4, 5] => 6}.each{|(x, *), *| p x}

{[1, 2] => 3, [4, 5] => 6}.each{|(x, _), _| p x}

{[1, 2, 3], [4, 5, 6]}.each{|*, x, *| p x}

{[1, 2, 3], [4, 5, 6]}.each{|_, x, _| p x}

def (x, *), *; p x; end

def (x, _), _; p x; end

def *, x, *; p x; end

def _, x, _; p x; end

What are the differences between them, and when should I use which? When there is need to mark multiple variables as unused as in the above examples, is either better?

回答1:

A * means "all remaining parameters". An _ is just another variable name, although it is a bit special. So they are different, for example the following does not make sense:

[[1, 2, 3], [4, 5, 6]].each{|*, x, *| p x}  # Syntax error

Indeed, how is Ruby supposed to know if the first star should get 0, 1 or 2 of the values (and the reverse)?

There are very few cases where you want to use a star to ignore parameters. An example would be if you only want to use the last of a variable number of parameters:

[[1], [2, 3], [4, 5, 6]].each{|*, last| p last}  # => prints 1, 3 and 6

Ruby allows you to not give a name to the "rest" of the parameters, but you can use _:

[[1], [2, 3], [4, 5, 6]].each{|*_, last| p last}  # => prints 1, 3 and 6

Typically, the number of parameters is known and your best choice is to use a _:

[[1, 2, 3], [4, 5, 6]].each{|_, mid, _| p mid}  # prints 2 and 5

Note that you could leave the last paramater unnamed too (like you can when using a *), although it is less obvious:

[[1, 2, 3], [4, 5, 6]].each{|_, mid, | p mid}  # prints 2 and 5

Now _ is the designated variable name to use when you don't want to use a value. It is a special variable name for two reasons:

  1. Ruby won't complain if you don't use it (if warnings are on)
  2. Ruby will allow you to repeat it in the argument list.

Example of point 1:

> ruby -w -e "def foo; x = 42; end; foo"
-e:1: warning: assigned but unused variable - x

> ruby -w -e "def foo; _ = 42; end; foo"
no warning

Example of point 2:

[[1, 2, 3], [4, 5, 6]].each{|unused, mid, unused| p mid}
# => SyntaxError: (irb):23: duplicated argument name

[[1, 2, 3], [4, 5, 6]].each{|_, mid, _| p mid}
# => prints 2 and 5

Finally, as @DigitalRoss notes, _ holds the last result in irb

Update: In Ruby 2.0, you can use any variable starting with _ to signify it is unused. This way the variable name can be more explicit about what is being ignored:

_scheme, _domain, port, _url = parse_some_url
# ... do something with port


回答2:

I think it's mostly stylistic and programmer's choice. Using * makes more sense to me in Ruby because its purpose is to accumulate all parameters passed from that position onward. _ is a vestigial variable that rarely sees use in Ruby, and I've heard comments that it needs to go away. So, if I was to use either, I'd use *.

SOME companies might define it in their programming style document, if they have one, but I doubt it's worth most of their time because it is a throw-away variable. I've been developing professionally for over 20 years, and have never seen anything defining the naming of a throw-away.

Personally, I don't worry about this and I'd be more concerned with the use of single-letter variables. Instead of either, I would use unused or void or blackhole for this purpose.



回答3:

IMO the practice makes code less readable, and less obvious.

Particularly in API methods taking blocks it may not be clear what the block actually expects. This deliberately removes information from the source, making maintenance and modification more difficult.

I'd rather the variables were named appropriately; in a short block it will be obvious it's not being used. In longer blocks, if the non-use is remarkable, a comment may elaborate on the reason.



回答4:

What are the differences between them?

In the _ case a local variable _ is being created. It's just like using x but named differently.

In the * case the assignment of an expression to * creates [expression]. I'm not quite sure what it's useful for as it doesn't seem to do anything that just surrounding the expression with brackets does.

When should I use which?

In the second case you don't end up with an extra symbol being created but it looks like slightly more work for the interpreter. Also, it's obvious that you will never use that result, whereas with _ one would have to read the loop to know if it's used.

But I predict that the quality of your code will depend on other things than which trick you use to get rid of unused block parameters. The * does have a certain obscure cool-factor that I kind of like.

Note: when experimenting with this, be aware that in irb, _ holds the value of the last expression evaluated.