What Is the Standalone Splat Operator (*) Used for in Ruby

What is the standalone splat operator (*) used for in Ruby?

In a parameter list, *args means "gobble up all the remaining arguments in an array and bind them to the parameter named args". * means "gobble up all the remaining arguments and bind them to nothing", or put more simply "ignore all remaining arguments".

And that's exactly when you would use this: when you want to ignore all the remaining arguments. Either because you don't care about them, or because you don't care about them (but someone else might):

def foo(*)
# do something
super
end

Remember: super without an argument list passes the arguments along unmodified. So, even though this override of foo ignored the arguments, they are still available to the superclass's implementations of the method; yet, the definition makes it clear that this implementation doesn't care.

Why is the splat used inside an array definition here?

It creates a single flat array for catch

I'm not sure anyone completely understands the splat operator. Many times it removes one level of "arrayness", but it won't remove the last level.

It is possible to get it in this one case, at least. It creates a single level of array for the catch parameter regardless of whether catch is a single number or an array of numbers.

>> t = [*404]
=> [404]
>> t = [*[404,405,406]]
=> [404, 405, 406]

Is it possible to invert a splat in Ruby?

  • Regarding *(inverse_splat(foo)), it does not make sense. The result of splatting is in general a sequence of objects, which is not an object. Such thing cannot exist in Ruby.

    And at this point, the assumption you seem to making, i.e., that the order of inverse_splat and * are interchangable, turns out to be false.

  • Regarding inverse_splat(*(foo))

    There cannot be such inverse. That is because the splat * internally calls to_a, which is not a one-to-one mapping.

    [[:a, 1], [:b, 2]].to_a
    # => [[:a, 1], [:b, 2]]
    {a: 1, b: 2}.to_a
    # => [[:a, 1], [:b, 2]]

    An inverse can only be defined on a one-to-one map.

    If you disregard such cases and want to still explore if there is something close to it, then a close thing is the [...] literal.

Using splat operator with when

But the splat operator is about assignment, not comparison.

In this case, * converts an array into an argument list:

when *[2, 3, 4]

is equivalent to:

when 2, 3, 4

Just like in a method call:

foo(*[2, 3, 4])

is equivalent to:

foo(2, 3, 4)

Ruby String end_with? Array

You're just missing the splat operator (*):

filetypes = %w(.flac .wv)

Dir.open(Dir.pwd).each do |filename|
pp(filename) if filename.end_with?(*filetypes)
end

Why is ❨╯°□°❩╯︵┻━┻ with such an encoding used for a method name?

If you really do not understand the sense of the method name, that is a (Japanese-style) facemark. Whereas English facemarks are rotated 90 degrees counter-clockwise and are long in the vertical direction of the actual face, Japanese facemarks are to be read in the direction as is, and are long in the horizontal direction. The author of this is likely to be either a Japanese, or someone who is influenced by Japanese culture like anime.

In this particular case, each character expresses a certain part. From left to right:

  • The right edge of the face
  • The right arm raised
  • ° The right eye
  • The mouth
  • ° The left eye
  • The left edge of the face
  • The left arm raised
  • An imaginary curve expressing the trace of a thrown table
  • ┻━┻ A thrown upside-down table (most likely a chabudai that used to be seen typically in Japanese homes until some decades ago)

Chabudai gaeshi used to happen often at some feudal Japanese homes until some decades ago. The father had the absolute monarchic right at home, and whenever he was frustrated over something, he would flip the chabudai during dinner to show his anger, and the family (especially the mother) had to prepare the dinner again.

chabudai gaeshi

Here are more variations.

What does ** (double star/asterisk) and * (star/asterisk) do for parameters?

The *args and **kwargs is a common idiom to allow arbitrary number of arguments to functions as described in the section more on defining functions in the Python documentation.

The *args will give you all function parameters as a tuple:

def foo(*args):
for a in args:
print(a)

foo(1)
# 1

foo(1,2,3)
# 1
# 2
# 3

The **kwargs will give you all
keyword arguments except for those corresponding to a formal parameter as a dictionary.

def bar(**kwargs):
for a in kwargs:
print(a, kwargs[a])

bar(name='one', age=27)
# name one
# age 27

Both idioms can be mixed with normal arguments to allow a set of fixed and some variable arguments:

def foo(kind, *args, **kwargs):
pass

It is also possible to use this the other way around:

def foo(a, b, c):
print(a, b, c)

obj = {'b':10, 'c':'lee'}

foo(100,**obj)
# 100 10 lee

Another usage of the *l idiom is to unpack argument lists when calling a function.

def foo(bar, lee):
print(bar, lee)

l = [1,2]

foo(*l)
# 1 2

In Python 3 it is possible to use *l on the left side of an assignment (Extended Iterable Unpacking), though it gives a list instead of a tuple in this context:

first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]

Also Python 3 adds new semantic (refer PEP 3102):

def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
pass

For example the following works in python 3 but not python 2:

>>> x = [1, 2]
>>> [*x]
[1, 2]
>>> [*x, 3, 4]
[1, 2, 3, 4]

>>> x = {1:1, 2:2}
>>> x
{1: 1, 2: 2}
>>> {**x, 3:3, 4:4}
{1: 1, 2: 2, 3: 3, 4: 4}

Such function accepts only 3 positional arguments, and everything after * can only be passed as keyword arguments.

Note:

  • A Python dict, semantically used for keyword argument passing, are arbitrarily ordered. However, in Python 3.6, keyword arguments are guaranteed to remember insertion order.
  • "The order of elements in **kwargs now corresponds to the order in which keyword arguments were passed to the function." - What’s New In Python 3.6
  • In fact, all dicts in CPython 3.6 will remember insertion order as an implementation detail, this becomes standard in Python 3.7.


Related Topics



Leave a reply



Submit