Hello I'm studying some Ruby code. Implement Quicksort in Ruby:
1 def qsort(lst)
2 return [] if lst.empty?
3 x, *xs = *lst
4 less, more = xs.partition{|y| y < x}
5 qsort(less) + [x] + qsort(more)
6 end
Given:
lst = [1, 2, 3, 4, 5]
x, *xs = *lst
I do not know if I understand what line 3 is doing correctly:
From my observation and experiment, this will assign 1 from lst to x, and the rest of lst to xs.
Also I found these two are doing the same thing:
x, *xs = *lst
is equivalent to
x, *xs = lst
My question is, what's the name of this nice feature (I will edit the title afterwards to adapt)? Then I could study more about this Ruby feature myself. Sorry if it's a duplicate problem, because I don't know the keyword to search on this problem.
The name of this feature is called splat operator in Ruby.
The splat operator in Ruby, Groovy and Perl allows you to switch between parameters and arrays:it splits a list in a series of parameters,or collects a series of parameters to fill an array.
From 4 Lines of Code.
This statement
x, *xs = *lst
doesn't make much sense to me, but these do:
x, *xs = [1, 2, 3] # x -> 1, xs -> [2, 3]
x = 1, *[2, 3, 4] # x -> [1, 2, 3, 4]
this usage IMO has nothing to do with parameters, but as others said splat can be (and usually is) used with parameters:
def foo(a, b, c)
end
foo(*[1,2,3]) # a -> 1, b -> 2, c -> 3
Related
I'm exploring Functional Programming with the Ruby language. Below is my version of a Fold in Ruby. I've tested it on a variety of functions, reverse, filter, map etc, and it returns the results as expected. But it mutates data and needs assignment statements. Can anyone help me to do the same but without violating the Functional paradigm? Can anyone help me with the partial application of the curried function at the bottom? I suspect there something obvious I'm missing. Thanks.
fold_l = lambda do |ray, base, funcky|
if ray == []
base
else
base = funcky.call(base,ray.first)
ray.shift
fold_l.call(ray,base,funcky)
end
end
abc = [1, 2, 3, 4, 5, 6, 7]
mapper = lambda {|sum, x| sum << x*x}
lengthy = lambda {|sum, _| sum + 1}
p fold_l.call(abc,[],mapper) ## works fine
p abc ## but mutates data!!
abc = [1, 2, 3, 4, 5, 6, 7]
p curryFold = fold_l.curry.(abc).(0).(lengthy) ## works fine
lengthC = curryFold.(base:0).(funcky:lengthy)
p lengthC.call.(abc) ## but this gives error
Rework your fold_l function to not mangle the arguments its given:
def fold_l(ray, base, funcky)
return base if ray.empty?
base = funcky.call(base,ray.first)
fold_l(ray.last(ray.length-1),base,funcky)
end
This uses last to return a copy of the arguments minus the first. It's also not necessary to use lambda here as you want a named function, so you may as well declare it formally. lambda is reserved for situations where you don't necessarily have a name for it.
Note that in Ruby it's generally rude to damage the arguments your method's given unless there's an understanding that it's acceptable. Most methods make copies if they need to perform alterations.
I would probably implement foldl like this – and always be careful when using recursion in languages that don't support tail call optimisation (read more)
foldl = -> (f, acc, (x,*xs)) do
if x.nil? then
acc
else
foldl.call f, (f.call acc, x), xs
end
end
add = -> (x,y) do
x + y
end
length =
foldl.curry
. (-> (acc,_) { acc + 1 })
. (0)
data = [ 1, 2, 3, 4, 5 ]
p foldl.call add, 0, data
# => 15
p length.call data
# => 5
as per #tadman's recommendation, using a normal def/end block is probably better but that's just a matter of preference – note that currying is no longer necessary with this style
def foldl f, acc, (x,*xs)
if x.nil? then
acc
else
foldl f, (f.call acc, x), xs
end
end
def add x, y
x + y
end
def length xs
foldl (-> (acc,_) { acc + 1 }), 0, xs
end
data = [ 1, 2, 3, 4, 5 ]
p foldl method(:add), 0, data
# => 15
p length data
# => 5
Since parallel assignment like this can be done,
x, y, z = [1, 2, 3]
I wanted to know if one could drop for example the y value. Is there a way of doing parallel assignment without polluting the namespace of the current scope?
I tried assigning to nil:
x, nil, z = [1, 2, 3]
but that does not work.
The idiomatic way to do that is to assign it to variable named underscore:
x, _, z = [1, 2, 3]
If there are multiple values that you want to drop, you can use splat:
x, *_, z = [1, 2, 3, 4, 5]
As mentioned by #ndn, _ is often used.
It's okay to ignore it and to reassign over it, but _ is still a variable in the namespace of the current scope :
l = [1, 2, 3]
x, _, z = l
puts _
#=> 2
If it bothers you, you could use :
x, z = l.values_at(0,-1)
It might be less readable though. _ is also the proposed syntax if you launch rubocop on the script.
Talking about readability, if you want to explain what the variable is, but still want to show that it won't be used afterwards, you could use :
x, _y, z = [1, 2, 3]
# Do something with x and z. Ignore _y
It's also proposed by rubocop :
test.rb:2:4: W: Useless assignment to variable - y. Use _ or _y as a variable name to indicate that it won't be used.
meschi has asked whether it is possible, using parallel assignment, to assign some, but not all, elements of an array to variables.
Firstly, it doesn't help to write
a, _, c = [1, true, 2]
since _ is a variable:
_ #=> true
(You won't get this result using IRB as IRB uses _ for its own purposes.)
If, in the above array, you only want to assign 1 and true to variables you can write
a, b, * = [1, true, 2]
a #=> 1
b #=> true
If you only want to assign 1 and 2 to variables, you can write
a, *, b = [1, true, 2]
a #=> 1
b #=> 2
If, for the array [1, true, 2, 3], you wish to assign only 1 and 2 to variables, well, that's a problem. Evidently, as #Eric pointed out in a comment, you'd need to write
a, b, b, * = [1, true, 2, 3]
a #=> 1
b #=> 2
This idea can of course be generalized:
a, b, b, b, c, c = [1, true, 2, 3, 4, 5]
a #=> 1
b #=> 3
c #=> 5
Is this a good idea? That was not the question.
The main concern may be creating a not-to-be-used variable and then inadvertently using it, as here:
a, _, b = [1, true, 2]
...
launch_missiles_now?(_)
If so, there are a few options. One is to simply wrap the expression in an method, thereby keeping the superfluous variables tightly-confined:
def get_a_and_b(arr)
a, _, b, _ = arr
[a, b]
end
a, b = get_a_and_b [1, true, 2, 3]
#=> [1, 2]
Another is to name unwanted variables to make it unlikely that they will referenced accidentally, for example:
a, _reference_this_variable_and_you_will_die, b = [1, true, 2]
I would prefer to improve #ndn answer and use only * symbol to drop values.
_ usually used to return last value, so it can be confusing to use it here.
So you can use
x, *, z = [1, 2, 3]
as well as
x, *, y, z = [1, 2, 3, 4]
and it will still understand you and return the proper values for all the variables.
I wanted to create a method for array's to get a splat of the array in return. Is this possible to do in Ruby?
For example here's my current code:
Array.module_eval do
def to_args
return *self
end
end
I expect [1,2,3].to_args to return 1,2,3 but it ends up returning [1,2,3]
You cannot return a "splat" from Ruby. But you can return an array and then splat it yourself:
def args
[1, 2, 3]
end
x, y, z = args
# x == 1
# y == 2
# z == 3
x, *y = args
# x == 1
# y == [2, 3]
Of course, this works on any array, so really there is no need for monkey patching a to_args method into Array - it's all about how the calling concern is using the splat operator:
arr = [1, 2, 3]
x, y, z = arr
x, *y = arr
*x, y = arr
Same mechanism works with block arguments:
arr = [1, 2, 3]
arr.tap {|x, *y| y == [2, 3]}
Even more advanced usage:
arr = [1, [2, 3]]
x, (y, z) = arr
The concept that clarifies this for me is that although you can simulate the return of multiple values in Ruby, a method can really return only 1 object, so that simulation bundles up the multiple values in an Array.
The array is returned, and you can then deconstruct it, as you can any array.
def foo
[1, 2]
end
one, two = foo
Not exactly. What it looks like you're trying to do (the question doesn't give usage examples) is to force multiple return values. However, returning the splatted array self may do exactly what you need, as long as you're properly handling multiple return values on the calling side of the equation.
Consider these examples:
first, *rest = [1, 2, 3] # first = 1, rest = [2, 3]
*rest, last = [1, 2, 3] # rest = [1, 2], last = 3
first, *rest, last = [1, 2, 3] # first = 1, rest = [2], last = 3
Other than this, I can't actually see any way to capture or pass along multiple values like you're suggesting. I think the answer for your question, if I understand it correctly, is all in the caller's usage.
I have the following knowledge base
eliminate(X,[X|T],T).
eliminate(X,[H|T],[H|T2]) :- eliminate(X,T,T2).
And I have to make the running process of an example by myself, without the interpreter (like a tree).
For example, if I post the query: eliminate(3,[1,2,3,4,5],Y).
First using the first fact, we unificate X=3, and with the second element, which is a list([1,2,3,4,5]) we try to unify 1 with X, but we can't because X now is 3, so it fails and we try with the second rule.
eliminate(3,[1,2,3,4,5],Y).
x = 3, H = 1, T = [2,3,4,5], H = Y , T2 = []
This it the part that I am not sure is right. I don't know why T2 has to be unified with [].
Now with the body clause of the second rule: eliminate(X,T,T2), I have:
eliminate(3,[2,3,4,5],[])
x = 3, (here it fails again, so i have to use the second rule)
eliminate(3,[2,3,4,5],[])
x = 3, H = 2, T = [3,4,5], H = Y, T2 =[] ??? is T2 null again???
T2 doesn't unify with [] the first time the second rule is applied. The resolution knows it is a list (because its on the right side of the [X|Y] construct), but doesn't know its value yet. To find its value, it will first compute the rule, which will find a value for T2 recursively, then unify this value with the Y you ran in your query.
In your example, running the query eliminate(3, [1, 2, 3, 4, 5], Y). will do the following:
Call eliminate(3, [1, 2, 3, 4, 5], Y)
2nd rule: X=3, H=1, T=[2,3,4,5], T2=?, Y=[H|?]
Call eliminate(3, [2, 3, 4, 5], ?)
2nd rule: X=3, H=2, T=[3,4,5], T2=??, Y=[H|??]
Call eliminate(3, [3, 4, 5], ??)
1st rule: ??=[4, 5]
Go back one step, using Y=[H|??], H=2 ??=[4,5]: Y = [2|[4,5]] = [2, 4, 5] = ?
Go back another step, using Y=[H|?], H=1, ?=[2, 4, 5]: Y = [1|[2, 4, 5]] = [1, 2, 4, 5]
I suggest spending some time reading the recusion chapter of your Prolog learning source. Additionally, to find out how you can see this execution in practice to see what's happening, you can use the trace. "command" or other debug equivalent, see this link for swi-pl specifics on debugging a program.
I need to define segment(X,Y), having X as a contiguous series of elements in Y, and I must use cut in my solution.
How should I use cut? If I use !, then I won't get true after the first header. This is what I have:
segment([],_).
segment([H|T],[H|Y]) :- segment(T,Y).
segment([H|T],[X|Y]) :- segment([H|T],Y).
This is my output
34 ?- segment(X,[1,2,3,4]).
X = []
Action? ;
X = [1] ;
X = [1, 2] ;
X = [1, 2, 3] ;
X = [1, 2, 3, 4] ;
X = [1, 2, 4] ;
X = [1, 3] ;
X = [1, 3, 4] ;
X = [1, 4] ;
X = [2] ;
X = [2, 3] ;
X = [2, 3, 4] ;
X = [2, 4] ;
X = [3] ;
X = [3, 4] ;
X = [4] ;
Your segment predicate enumerates the sub-sequences of the original list, including "holes" in them.
To produce only contiguous segments of the list, conceptually there are two stages - first we skip along the list, then we collect some of it elements. So we must have two separate predicates for that:
segment([],[]).
segment(X,[_|L]):- segment(...). % we either skip,
segment(X,L):- seg_get(X,L). % or get some elements
seg_get([A|B],[A|D]):- seg_get(...). % we get some elements
seg_get([A],[A|_]). % or we stop whenever we feel like it
I don't see a way to have it all packaged into just one predicate with the same arguments by adding a cut somewhere. That is because the choice in seg_get must be only between its two current possibilities, and if we just rename it to segment, it will be able to backtrack to other possibilities above it.
In some cases you have to use cut to get your results but there is possibility to do this without it. Here is my solution with a cut, hope it helps.
segment([],_).
segment([H|T],[H|Y]) :- segmenthelperino(T,Y), !.
segment([H|T],[_|Y]) :- segment([H|T],Y).
// This is the helper function
segmenthelperino([],_).
segmenthelperino([H|T],[H|Y]) :- segmenthelperino(T,Y).
cheers..