What does a * in front of a string literal do in ruby? - ruby

This code seems to create an array with a range from a to z but I don't understand what the * does. Can someone please explain?
[*"a".."z"]

It's called splat operator.
Splatting an Lvalue
A maximum of one lvalue may be splatted in which case it is assigned an Array consisting of the remaining rvalues that lack corresponding lvalues. If the rightmost lvalue is splatted then it consumes all rvalues which have not already been paired with lvalues. If a splatted lvalue is followed by other lvalues, it consumes as many rvalues as possible while still allowing the following lvalues to receive their rvalues.
*a = 1
a #=> [1]
a, *b = 1, 2, 3, 4
a #=> 1
b #=> [2, 3, 4]
a, *b, c = 1, 2, 3, 4
a #=> 1
b #=> [2, 3]
c #=> 4
Empty Splat
An lvalue may consist of a sole asterisk (U+002A) without any associated identifier. It behaves as described above, but instead of assigning the corresponding rvalues to the splatted lvalue, it discards them.
a, *, b = *(1..5)
a #=> 1
b #=> 5
Splatting an Rvalue
When an rvalue is splatted it is converted to an Array with Kernel.Array(), the elements of which become rvalues in their own right.
a, b = *1
a #=> 1
b #=> nil
a, b = *[1, 2]
a #=> 1
b #=> 2
a, b, c = *(1..2), 3
a #=> 1
b #=> 2
c #=> 3

The splat operator expands the range into an array.

Huh, fun fact. When you do this:
*(0..50)
you get an error.
The splat operator, in this case, requires a receiver in order to work. So don't fool yourself into thinking its broken in irb by just trying it without a receiver.

Related

Why can I assign two variables corresponding to an array in Ruby?

After about a year of Ruby, I just saw this somewhere and my mind is blown. Why in the world does this work?
>> words = ['uno', 'dos']
=> ["uno", "dos"]
>> first, second = words
=> ["uno", "dos"]
>> first
=> "uno"
>> second
=> "dos"
Specifically, how does this work:
>> first, second = ['uno', 'dos']
Why can I do this? It makes no syntactical sense!
It makes no syntactical sense
But this is part of Ruby's syntax! In the Ruby docs it is known as array decomposition:
Like Array decomposition in method arguments you can decompose an
Array during assignment using parenthesis:
(a, b) = [1, 2]
p a: a, b: b # prints {:a=>1, :b=>2}
You can decompose an Array as part of a larger multiple assignment:
a, (b, c) = 1, [2, 3]
p a: a, b: b, c: c # prints {:a=>1, :b=>2, :c=>3}
Since each decomposition is considered its own multiple assignment you
can use * to gather arguments in the decomposition:
a, (b, *c), *d = 1, [2, 3, 4], 5, 6
p a: a, b: b, c: c, d: d
# prints {:a=>1, :b=>2, :c=>[3, 4], :d=>[5, 6]}
Edit
as Stefan points out in the comments, the docs don't mention that array decomposition also occurs implicitly (i.e. without parenthesis) if there is only one value on the right-hand side:
a, b = [1, 2] works like (a, b) = [1, 2]
Why can I do this? It makes no syntactical sense!
It makes a perfect sense. It is an example of parallel assignment.
When you use = what is happening is each of the list of variables on the left of = are assigned to each of the list of expressions on the right of =.
first, second = ['uno', 'dos']
# is equivalent to
first, second = 'uno', 'dos'
If there are more variables on the left, than expressions on the right, those left variables are assigned with nil:
first, second = 'uno'
first #=> 'uno'
second #=> nil
As to
words = ['uno', 'dos']
first, second = words
first #=> 'uno'
second #=> 'dos'
It is not assigning the whole words array to first leaving second with nil, because while parallel assignment Ruby tries to decompose the right side expression, and does so if it is an instance of Array.
[TIL] Moreover, it attempts to call to_ary on the right side expression, and if it responds to the method, decomposes accordingly to that object's to_ary implementation (credits to #Stefan):
string = 'hello world'
def string.to_ary; split end
first, second = string
first #=> 'hello'
second #=> 'world'
This is called multiple assignment, handy to assign multiple variables at once.
example
one, two = 1,2
puts one #=>1
puts two #=>2
one, two = [1,2] # this makes sense
one, two = 1 # obviously this doesn't it will assign nil to two
Hope its bit clear now

Why does the Ruby splat not work for array coercion in conditional assignment?

Although the splat (*) construct is commonly referred to as the splat operator, it is clear that it is a different beast, compared to other unary operators like the negation (!) operator.
The splat works fine on it's own (i.e. not wrapped in brackets) when used in assignment (=), but produces an error when used with conditional assignment (||=). Example:
a = *(1..3)
#=> [1, 2, 3]
b ||= *(1..3)
SyntaxError: (irb):65: syntax error, unexpected *
I am not looking for alternative ways of doing the same thing, but looking for someone with a better understanding of the Ruby internals to explain why this usage of the splat construct works in the first case but not in the second.
Here's my understanding of the practical goal of splat. This is for Ruby 2.2 MRI/KRI/YARV.
Ruby splat destructures an object into an array during assignment.
These examples all provide the same result, when a is falsey:
a = *(1..3)
a = * (1..3)
a =* (1..3)
a = *1..3
a = * 1..3
a = * a || (1..3)
a = * [1, 2, 3]
=> [1, 2, 3]
The splat does the destructuring during the assigment, as if you wrote this:
a = [1, 2, 3]
(Note: the splat calls #to_a. This means that when you splat an array, there's no change. This also means that you can define your own kinds of destructuring for any class of your own, if you wish.)
But these statements fail:
*(1..3)
* 1..3
* [1,2,3]
false || *(1..3)
x = x ? x : *(1..3)
=> SyntaxError
These statements fail because there's no assignment happening exactly when the splat occurs.
Your question is this special case:
b ||= *(1..3)
Ruby expands this to:
b = b || *(1..3)
This statement fails because there's no assignment happening exactly when the splat occurs.
If you need to solve this in your own code, you can use a temp var, such as:
b ||= (x=*(1..3))
Worth mentioning: there's an entirely different use of splat when it's on the left hand side of the expression. This splat is a low-priority greedy collector during parallel assignment.
Examples:
*a, b = [1, 2, 3] #=> a is [1, 2], b is 3
a, *b = [1, 2, 3] #=> a is 1, b is [2, 3]
So this does parse:
*a = (1..3) #=> a is (1..3)
It sets a to all the results on the right hand side, i.e. the range.
In the rare case that the splat can be understood as either a destructurer or a collector, then the destructurer has precendence.
This line:
x = * y = (1..3)
Evaluates to this:
x = *(y = (1..3))
Not this:
x = (*y = (1..3))
By splatting *(1..3) in your expression you get 1, 2, 3, and when it is assigned it behaves like mass assignment, but ruby seems not to support it for conditional assignment.
I.e. a=1,2,3 is just a syntax sugar of ruby. Just use an array explicitly:
a ||= [*1..3] #=> [1, 2, 3]
And actually you only use part of splat functionality here - it's autoconversion to array :) so you can simply do:
a ||= (1..3).to_a #=> [1, 2, 3]

How does Ruby return two values?

Whenever I swap values in an array, I make sure I stored one of the values in a reference variable. But I found that Ruby can return two values as well as automatically swap two values. For example,
array = [1, 3, 5 , 6 ,7]
array[0], array[1] = array[1] , array[0] #=> [3, 1]
I was wondering how Ruby does this.
Unlike other languages, the return value of any method call in Ruby is always an object. This is possible because, like everything in Ruby, nil itself is an object.
There's three basic patterns you'll see. Returning no particular value:
def nothing
end
nothing
# => nil
Returning a singular value:
def single
1
end
x = single
# => 1
This is in line with what you'd expect from other programming languages.
Things get a bit different when dealing with multiple return values. These need to be specified explicitly:
def multiple
return 1, 2
end
x = multiple
# => [ 1, 2 ]
x
# => [ 1, 2 ]
When making a call that returns multiple values, you can break them out into independent variables:
x, y = multiple
# => [ 1, 2 ]
x
# => 1
y
# => 2
This strategy also works for the sorts of substitution you're talking about:
a, b = 1, 2
# => [1, 2]
a, b = b, a
# => [2, 1]
a
# => 2
b
# => 1
No, Ruby doesn't actually support returning two objects. (BTW: you return objects, not variables. More precisely, you return pointers to objects.)
It does, however, support parallel assignment. If you have more than one object on the right-hand side of an assignment, the objects are collected into an Array:
foo = 1, 2, 3
# is the same as
foo = [1, 2, 3]
If you have more than one "target" (variable or setter method) on the left-hand side of an assignment, the variables get bound to elements of an Array on the right-hand side:
a, b, c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary[2]
If the right-hand side is not an Array, it will be converted to one using the to_ary method
a, b, c = not_an_ary
# is the same as
ary = not_an_ary.to_ary
a = ary[0]
b = ary[1]
c = ary[2]
And if we put the two together, we get that
a, b, c = d, e, f
# is the same as
ary = [d, e, f]
a = ary[0]
b = ary[1]
c = ary[2]
Related to this is the splat operator on the left-hand side of an assignment. It means "take all the left-over elements of the Array on the right-hand side":
a, b, *c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary.drop(2) # i.e. the rest of the Array
And last but not least, parallel assignments can be nested using parentheses:
a, (b, c), d = ary
# is the same as
a = ary[0]
b, c = ary[1]
d = ary[2]
# which is the same as
a = ary[0]
b = ary[1][0]
c = ary[1][1]
d = ary[2]
When you return from a method or next or break from a block, Ruby will treat this kind-of like the right-hand side of an assignment, so
return 1, 2
next 1, 2
break 1, 2
# is the same as
return [1, 2]
next [1, 2]
break [1, 2]
By the way, this also works in parameter lists of methods and blocks (with methods being more strict and blocks less strict):
def foo(a, (b, c), d) p a, b, c, d end
bar {|a, (b, c), d| p a, b, c, d }
Blocks being "less strict" is for example what makes Hash#each work. It actually yields a single two-element Array of key and value to the block, but we usually write
some_hash.each {|k, v| }
instead of
some_hash.each {|(k, v)| }
tadman and Jörg W Mittag know Ruby better than me, and their answers are not wrong, but I don't think they are answering what OP wanted to know. I think that the question was not clear though. In my understanding, what OP wanted to ask has nothing to do with returning multiple values.
The real question is, when you want to switch the values of two variables a and b (or two positions in an array as in the original question), why is it not necessary to use a temporal variable temp like:
a, b = :foo, :bar
temp = a
a = b
b = temp
but can be done directly like:
a, b = :foo, :bar
a, b = b, a
The answer is that in multiple assignment, the whole right hand side is evaluated prior to assignment of the whole left hand side, and it is not done one by one. So a, b = b, a is not equivalent to a = b; b = a.
First evaluating the whole right hand side before assignment is a necessity that follows from adjustment when the both sides of = have different numbers of terms, and Jörg W Mittag's description may be indirectly related to that, but that is not the main issue.
Arrays are a good option if you have only a few values. If you want multiple return values without having to know (and be confused by) the order of results, an alternative would be to return a Hash that contains whatever named values you want.
e.g.
def make_hash
x = 1
y = 2
{x: x, y: y}
end
hash = make_hash
# => {:x=>1, :y=>2}
hash[:x]
# => 1
hash[:y]
# => 2
Creating a hash as suggested by some is definitely better than array as array indexing can be confusing. When an additional attribute needs to be returned at a certain index, we'll need to make changes to all the places where the return value is used with array.
Another better way to do this is by using OpenStruct. Its advantage over using a hash is its ease of accessibility.
Example: computer = OpenStruct.new(ram: '4GB')
there are multiple ways to access the value of ram
as a symbol key: computer[:ram]
as a string key: computer['ram']
as an attribute(accessor method): computer.ram
Reference Article: https://medium.com/rubycademy/openstruct-in-ruby-ab6ba3aff9a4

What does a comma followed by an equals sign mean in Ruby?

Just saw something like this in some Ruby code:
def getis;gets.split.map(&:to_i);end
k,=getis # What is this line doing?
di=Array::new(k){Array::new(k)}
It assigns the array's first element using Ruby's multiple assignment:
a, = [1, 2, 3]
a #=> 1
Or:
a, b = [1, 2, 3]
a #=> 1
b #=> 2
You can use * to fetch the remaining elements:
a, *b = [1, 2, 3]
a #=> 1
b #=> [2, 3]
Or:
*a, b = [1, 2, 3]
a #=> [1, 2]
b #=> 3
It works like this. If lhs has single element and rhs has multiple values then lhs gets assigned an array of values, like this.
a = 1,2,3 #=> a = [1,2,3]
Whereas if lhs has more elements than rhs, then excess elements in lhs are discarded
a,b,c = 1,2 #=> a = 1, b = 2, c = nil
Therefore
a, = 1,2,3 #=> a = 1. The rest i.e. [2,3] are discarded

Ruby Method similar to Haskells cycle

Is there a Ruby method similar to Haskell's cycle? Haskell's cycle takes a list and returns that list infinitely appended to itself. It's commonly used with take which grabs a certain number of elements off the top of an array. Is there a Ruby method that takes an array and returns the array appended to itself some n number of times?
Yes, it's called cycle. From the documentation:
Array.cycle
(from ruby core)
------------------------------------------------------------------------------
ary.cycle(n=nil) {|obj| block } -> nil
ary.cycle(n=nil) -> an_enumerator
------------------------------------------------------------------------------
Calls block for each element repeatedly n times or forever if none
or nil is given. If a non-positive number is given or the array is empty, does
nothing. Returns nil if the loop has finished without getting interrupted.
If no block is given, an enumerator is returned instead.
a = ["a", "b", "c"]
a.cycle {|x| puts x } # print, a, b, c, a, b, c,.. forever.
a.cycle(2) {|x| puts x } # print, a, b, c, a, b, c.
Edit:
It seems like whats inside the block is basically a "Lambda", and as far as I know, I can't make a lambda concat each element onto an existing array.
b = [1, 2, 3]
z = []
b.cycle(2) { |i| z << i }
z # => [1, 2, 3, 1, 2, 3]
You can multiply an array by an integer using Array#*:
ary * int → new_ary
[...] Otherwise, returns a new array built by concatenating the int copies of self.
So you can do things like this:
>> [1, 2] * 3
=> [1, 2, 1, 2, 1, 2]

Resources