What does this mean in Ruby language? - ruby

Run the following code,
a = [1, 2, 3, 4, 5]
head, *tail = a
p head
p tail
You will get the result
1
[2, 3, 4, 5]
Who can help me to explain the statement head,*tail = a, Thanks!

head, *tail = a means to assign the first element of the array a to head, and assign the rest of the elements to tail.
*, sometimes called the "splat operator," does a number of things with arrays. When it's on the left side of an assignment operator (=), as in your example, it just means "take everything left over."
If you omitted the splat in that code, it would do this instead:
head, tail = [1, 2, 3, 4, 5]
p head # => 1
p tail # => 2
But when you add the splat to tail it means "Everything that didn't get assigned to the previous variables (head), assign to tail."

First, it is a parallel assignment. In ruby you can write
a,b = 1,2
and a will be 1 and b will be 2. You can also use
a,b = b,a
to swap values (without the typical temp-variable needed in other languages).
The star * is the pack/unpack operator. Writing
a,b = [1,2,3]
would assign 1 to a and 2 to b. By using the star, the values 2,3 are packed into an array and assigned to b:
a,*b = [1,2,3]

I don't know Ruby at all, but my guess is that the statement is splitting the list a into a head (first element) and the rest (another list), assigning the new values to the variables head and tail.
This mechanism is usually referred (at least in Erlang) as pattern matching.

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

Block with two parameters

I found this code by user Hirolau:
def sum_to_n?(a, n)
a.combination(2).find{|x, y| x + y == n}
end
a = [1, 2, 3, 4, 5]
sum_to_n?(a, 9) # => [4, 5]
sum_to_n?(a, 11) # => nil
How can I know when I can send two parameters to a predefined method like find? It's not clear to me because sometimes it doesn't work. Is this something that has been redefined?
If you look at the documentation of Enumerable#find, you see that it accepts only one parameter to the block. The reason why you can send it two, is because Ruby conveniently lets you do this with blocks, based on it's "parallel assignment" structure:
[[1,2,3], [4,5,6]].each {|x,y,z| puts "#{x}#{y}#{z}"}
# 123
# 456
So basically, each yields an array element to the block, and because Ruby block syntax allows "expanding" array elements to their components by providing a list of arguments, it works.
You can find more tricks with block arguments here.
a.combination(2) results in an array of arrays, where each of the sub array consists of 2 elements. So:
a = [1,2,3,4]
a.combination(2)
# => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
As a result, you are sending one array like [1,2] to find's block, and Ruby performs the parallel assignment to assign 1 to x and 2 to y.
Also see this SO question, which brings other powerful examples of parallel assignment, such as this statement:
a,(b,(c,d)) = [1,[2,[3,4]]]
find does not take two parameters, it takes one. The reason the block in your example takes two parameters is because it is using destruction. The preceding code a.combination(2) gives an array of arrays of two elements, and find iterates over it. Each element (an array of two elements) is passed at a time to the block as its single parameter. However, when you write more parameters than there is, Ruby tries to adjust the parameters by destructing the array. The part:
find{|x, y| x + y == n}
is a shorthand for writing:
find{|(x, y)| x + y == n}
The find function iterates over elements, it takes a single argument, in this case a block (which does take two arguments for a hash):
h = {foo: 5, bar: 6}
result = h.find {|k, v| k == :foo && v == 5}
puts result.inspect #=> [:foo, 5]
The block takes only one argument for arrays though unless you use destructuring.
Update: It seems that it is destructuring in this case.

Left-hand side and right-hand side in multiple assignment

I'm having trouble understanding these two sections in ruby-doc:
Implicit Array Assignment
Multiple Assignment
When it says left-hand side, the splat operator is on the right side, and when it says right-hand side, the operator is on the left side. For example:
The * can appear anywhere on the right-hand side:
*a, b = 1, 2, 3
p a: a, b: b # prints {:a=>[1, 2], :b=>3}
Can anyone explain me what the meaning of left-hand side and right-hand side is in these sections? To me, examples seem contradictory.
I think this is a mistake in this v2.0.0 reference manual. Your understanding is correct.
Both have been fixed in the v2.2.0 manual (Implicit Array Assignment and Multiple Assignment).
I think it is supposed to mean what you have in mind. However, the document looks like it has mistakes. You can report this to the developers here as a documentation bug.
* will turn an argument list in to an array and visa versa:
def do_it(*args)
args
end
do_it(1, 'hello') # => [1, 'hello']
In the case of *a, b = 1, 2, 3 it is processed right to left, b is assigned 3 and the remaining arguments, 2 and 3 to a as an array, [2, 3].
In the case of a = 1, *[2,3], the array, [2, 3], is converted to arguments 2, 3. Therefore equivalent to a = 1, 2, 3.
Why a = 1, 2, 3 is valid and does not cause an error I do not know. I would guess, Ruby does an implicit splat when you supply multiple arguments to a single var assignment. So a = [1, 2, 3] is functionaly the same as a = 1, 2, 3.

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]

Why is this Haskell incorrect?

I have a Haskell file which looks like this:
longest::[Integer]->[Integer]->[Integer]
max a b = if length a > length b then a else b
llfs::[Integer]->Integer
llfs li = length(foldl longest group(li))
llfs([1, 2, 3, 3, 4, 5, 1, 1, 1])
Which should print out the result of the function call at the end, however when I run the file I get this error:
parse error (possibly incorrect indentation)
I don't understand what I'm doing wrong. What should I do to fix it?
Edit
After putting the last line inside the main function, like this:
import List
longest::[Integer]->[Integer]->[Integer]
longest a b = if length a > length b then a else b
llfs::[Integer]->Integer
llfs li = length(foldl longest group(li))
main = print (llfs [1, 2, 3, 3, 4, 5, 1, 1, 1])
I now get the following error:
C:\Users\Martin\Desktop\Haskell\Q1.hs:7:31:
Couldn't match expected type `[Integer]'
against inferred type `[a] -> [[a]]'
In the second argument of `foldl', namely `group'
In the first argument of `length', namely
`(foldl longest group (li))'
In the expression: length (foldl longest group (li))
This one looks a little more difficult! How do I solve it?
Your code isn't correct.
This
longest::[Integer]->[Integer]->[Integer]
max a b = if length a > length b then a else b
should be
longest::[Integer]->[Integer]->[Integer]
longest a b = if length a > length b then a else b
And you need a main function
main = do print llfs([1, 2, 3, 3, 4, 5, 1, 1, 1])
Just for the sake of improving your code, if you have a function signature and give it a lower case letter(s) as its type (say the letter a), it becomes generic. For example
longest:: [a] -> [a] -> [a]
longest x y = if length x > length y then x else y
Means that rather than just working on lists of Integers, it works on lists of anything. Suddenly you have a very reusable function.
In the line
llfs li = length(foldl longest group(li))
the interpreter is treating group as the second argument to foldl. Writing group(li) is no different from writing group li.
Also, foldl needs an initial value. foldl1, on the other hand, uses the first list element for its initial value. Try:
llfs li = length (foldl1 longest (group li))
(Edited to remove the first, wrong answer.)
module Main where
import Data.List
longest::[Integer]->[Integer]->[Integer]
longest a b = if length a > length b then a else b
llfs::[Integer]->Int
llfs li = length $ foldl1 longest $ group li
main = do
putStrLn $ show $ llfs [1, 2, 3, 3, 4, 5, 1, 1, 1]
The problem was that the last line did not define a function, as others have stated. More things are wrong in your code. It appears this is what you want to do:
import Data.List
longest_group_size :: [Integer] -> Int
longest_group_size = maximum . map length . group
main :: IO ()
main = print $ longest_group_size [1, 2, 3, 3, 4, 5, 1, 1, 1]
Observe that:
You need to import Data.List in order to use group.
No need to use foldr in this case: by using map the length of each group is only calculated once.
This does mean, of course, that we call in the help of another function, maximum.
You cannot call a function at file scope like you would do in python or other scripting languages. Therefore the "call" to llfs in the last line is an error. Try printing the result in main:
main = print (llfs [1, 2, 3, 3, 4, 5, 1, 1, 1])
At the moment the "function call" looks like an incomplete function definition, where the right side is missing, which leads to the surprising error message:
llfs (...) = abc
The problem is this line:
llfs([1, 2, 3, 3, 4, 5, 1, 1, 1])
That's not a function declaration. I think you're trying to make a function call, in which case you need to put it inside a main declaration. You can also load the Haskell file into an interpreter (e.g., ghci) and execute the function call in the interpreter console.
This isn't the direct cause of either error, but I think it's a contributing factor to your misunderstanding. In Haskell, you would never write group(li). Parenthesizing a single argument is pointless — it's exactly equivalent to group li. If you're trying to pass the result of this function call to another function, you need to parenthesize the whole expression — (group li) — or use the $ operator like Caleb suggested.
Two small issues with the update. First, it looks like you're trying to pass the group result as an argument to foldl. The right way to say that is (group li) rather than group(li) The second is that foldl needs a base case. Caleb's suggestion to use foldl1 is one option that will probably work for you.

Resources