Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Ruby (http://www.velocityreviews.com/forums/f66-ruby.html)
-   -   Parallel Assignments and Elegance/Complexity Ratio. (http://www.velocityreviews.com/forums/t865858-parallel-assignments-and-elegance-complexity-ratio.html)

Parallel Assignments and Elegance/Complexity Ratio.

In SICP, I read that "Programs should be written for people to read, and
only incidentally for machines to execute".

While reading David/Matz book, I stumbled upon parallel assignments and
I thought the language was trying to be too flexible (adding complexity,
at least for a newcomer). My head soon started
spinning (when it reached a, (b, (c,d)) = 1, [2, [3, 4]] I was
exhausted).

My experience is recorded here:

Summary is, I should only spend time learning
- x, y, z = 1, 2, 3 (# => x=1, y=1, z=3), and
- x, y = y, x (# => swap x and y)

I gather that this might be a matter of taste and style, but are other
variants used by community?

Thank you,
Kedar

--
Posted via http://www.ruby-forum.com/.

 Robert Klemme 01-11-2011 03:30 PM

Re: Parallel Assignments and Elegance/Complexity Ratio.

On Tue, Jan 11, 2011 at 3:29 PM, Kedar Mhaswade
> In SICP, I read that "Programs should be written for people to read, and
> only incidentally for machines to execute".
>
> While reading David/Matz book, I stumbled upon parallel assignments and
> I thought the language was trying to be too flexible (adding complexity,
> at least for a newcomer). My head soon started
> spinning (when it reached a, (b, (c,d)) = 1, [2, [3, 4]] I was
> exhausted).
>
> My experience is recorded here:
>
> Summary is, I should only spend time learning
> - x, y, z = 1, 2, 3 (# => x=1, y=1, z=3), and
> - x, y = y, x (# => swap x and y)
>
> I gather that this might be a matter of taste and style, but are other
> variants used by community?

Just today I used

def []=(*idx, val)
index_check(idx)
@data[idx] = val
end

https://gist.github.com/772827

I find the pattern matching _very_ elegant. This also comes in handy
in situations like this:

irb(main):001:0> a = Array.new(10) { [rand(10), rand(10)] }
=> [[2, 3], [7, 8], [4, 0], [4, 6], [5, 2], [9, 9], [4, 2], [8, 5],
[2, 6], [5, 9]]
irb(main):002:0> a.sort {|(a1,a2),(b1,b2)| x = a2 <=> b2; x == 0 ? a1
<=> b1 : x}
=> [[4, 0], [4, 2], [5, 2], [2, 3], [8, 5], [2, 6], [4, 6], [7, 8],
[5, 9], [9, 9]]

Although in this particular case you could also use

irb(main):004:0> a.sort_by {|arr| arr.reverse}
=> [[4, 0], [4, 2], [5, 2], [2, 3], [8, 5], [2, 6], [4, 6], [7, 8],
[5, 9], [9, 9]]
irb(main):005:0> a.sort_by {|x, y| [y, x]}
=> [[4, 0], [4, 2], [5, 2], [2, 3], [8, 5], [2, 6], [4, 6], [7, 8],
[5, 9], [9, 9]]

Generally I'd say I don't use it overly often but when I use it it is
really handy. With #inject it's also useful if you iterate through a
collection of Arrays of known equal length:

irb(main):006:0> a.inject(0) {|s,(x,y)| s + x + y}
=> 100

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Re: Parallel Assignments and Elegance/Complexity Ratio.

Robert Klemme wrote in post #973945:
> On Tue, Jan 11, 2011 at 3:29 PM, Kedar Mhaswade
>>

>

>>
>> Summary is, I should only spend time learning
>> - x, y, z = 1, 2, 3 (# => x=1, y=1, z=3), and
>> - x, y = y, x (# => swap x and y)
>>
>> I gather that this might be a matter of taste and style, but are other
>> variants used by community?

>
> Just today I used
>
> def []=(*idx, val)
> index_check(idx)
> @data[idx] = val
> end
>
> https://gist.github.com/772827
>
>
> I find the pattern matching _very_ elegant. This also comes in handy
> in situations like this:

Maybe I am not understanding it, but I thought that elegance is because
of splat operator (which I am sure I like). My gripe is about various
forms of parallel assignment and semantic/syntactic complexity because
of that. Or are you saying that once you say you need splat operator,
all this complexity is inevitable (of course, I can work around it by
not using it, but then how do you all use it?)

-Kedar

>
> irb(main):001:0> a = Array.new(10) { [rand(10), rand(10)] }
> => [[2, 3], [7, 8], [4, 0], [4, 6], [5, 2], [9, 9], [4, 2], [8, 5],
> [2, 6], [5, 9]]
> irb(main):002:0> a.sort {|(a1,a2),(b1,b2)| x = a2 <=> b2; x == 0 ? a1
> <=> b1 : x}
> => [[4, 0], [4, 2], [5, 2], [2, 3], [8, 5], [2, 6], [4, 6], [7, 8],
> [5, 9], [9, 9]]
>
> Although in this particular case you could also use
>
> irb(main):004:0> a.sort_by {|arr| arr.reverse}
> => [[4, 0], [4, 2], [5, 2], [2, 3], [8, 5], [2, 6], [4, 6], [7, 8],
> [5, 9], [9, 9]]
> irb(main):005:0> a.sort_by {|x, y| [y, x]}
> => [[4, 0], [4, 2], [5, 2], [2, 3], [8, 5], [2, 6], [4, 6], [7, 8],
> [5, 9], [9, 9]]
>
> Generally I'd say I don't use it overly often but when I use it it is
> really handy. With #inject it's also useful if you iterate through a
> collection of Arrays of known equal length:
>
> irb(main):006:0> a.inject(0) {|s,(x,y)| s + x + y}
> => 100
>
> Kind regards
>
> robert

--
Posted via http://www.ruby-forum.com/.

 Josh Cheek 01-11-2011 07:58 PM

Re: Parallel Assignments and Elegance/Complexity Ratio.

[Note: parts of this message were removed to make it a legal post.]

> In SICP, I read that "Programs should be written for people to read, and
> only incidentally for machines to execute".
>
> While reading David/Matz book, I stumbled upon parallel assignments and
> I thought the language was trying to be too flexible (adding complexity,
> at least for a newcomer). My head soon started
> spinning (when it reached a, (b, (c,d)) = 1, [2, [3, 4]] I was
> exhausted).
>
> My experience is recorded here:
>
>
> Summary is, I should only spend time learning
> - x, y, z = 1, 2, 3 (# => x=1, y=1, z=3), and
> - x, y = y, x (# => swap x and y)
>
> I gather that this might be a matter of taste and style, but are other
> variants used by community?
>
> Thank you,
> Kedar
>
> --
> Posted via http://www.ruby-forum.com/.
>
>

I don't use it very often, but when I do, it usually makes an elegant
solution. I think part of the reason it doesn't seem that way is because you
are playing with it in too sterile of an environment. For example, you rate
"x, (y, (z, a))=[1, [2, [3, 4]]]" as lowest, suggesting it is equivalent to
"x=1;y=2;z=3;a=4" but this is not true. If you are actually assigning with
literals, you would, of course, use the equivalent way, but if your data
comes in as nested arrays, then you can't assign like that, instead you have
to do something like this:

def parallel(values)
x, (y, (z, a))=values
[x,y,z,a]
end
def alternative(values)
x = values.shift
values = values.shift
y = values.shift
values = values.shift
z = values.shift
a = values.shift
[x,y,z,a]
end
parallel [1, [2, [3, 4]]] # => [1, 2, 3, 4]
alternative [1, [2, [3, 4]]] # => [1, 2, 3, 4]

Now, I don't normally store data like that, so I haven't ever done anything
quite that fancy, but I use arrays on the RHS on occasion. It might look
something like this (though I don't normally store my data like this, either
-- it's really hard to think of a decent example!).

\$stdin = DATA
while input = gets
name , num = input.split
puts "#{name.capitalize}'s favourite number is #{num}"
end
__END__
josh 12
bill 42
sally 13
ned 99
clara 1000000

The alternative of
name , num = input.split
is
values = input.split
name = values.shift
num = values

I consider the former to be much more elegant as it avoids a temporary
variable.

Re: Parallel Assignments and Elegance/Complexity Ratio.

> literals, you would, of course, use the equivalent way, but if your data
> comes in as nested arrays, then you can't assign like that, instead you
> have
> to do something like this:
>
> def parallel(values)
> x, (y, (z, a))=values
> [x,y,z,a]
> end
> def alternative(values)
> x = values.shift
> values = values.shift
> y = values.shift
> values = values.shift
> z = values.shift
> a = values.shift
> [x,y,z,a]
> end
> parallel [1, [2, [3, 4]]] # => [1, 2, 3, 4]
> alternative [1, [2, [3, 4]]] # => [1, 2, 3, 4]
>
> Now, I don't normally store data like that, so I haven't ever done
> anything
> quite that fancy, but I use arrays on the RHS on occasion. It might look
> something like this (though I don't normally store my data like this,
> either
> -- it's really hard to think of a decent example!).

Thank you for sharing the use case. In this case, it helps. But like you
said, it seems like an answer in search of a question.

>
> \$stdin = DATA
> while input = gets
> name , num = input.split
> puts "#{name.capitalize}'s favourite number is #{num}"
> end
> ...
>
> The alternative of
> name , num = input.split
> is
> values = input.split
> name = values.shift
> num = values
>
> I consider the former to be much more elegant as it avoids a temporary
> variable.

This however is already covered by me as the first case (with HIGH
elegance rating) since you are expecting the line to contain exactly two
strings (and if it is not so, it's an exceptional situation) and hence
the (expected) number of lvalues = number of rvalues. I do
like/understand such application of parallel assignment.

Thanks, again!

--
Posted via http://www.ruby-forum.com/.

 Josh Cheek 01-11-2011 09:37 PM

Re: Parallel Assignments and Elegance/Complexity Ratio.

[Note: parts of this message were removed to make it a legal post.]

> > variable.
> > I consider the former to be much more elegant as it avoids a temporary

> This however is already covered by me as the first case (with HIGH
> elegance rating) since you are expecting the line to contain exactly two
> strings (and if it is not so, it's an exceptional situation) and hence
> the (expected) number of lvalues = number of rvalues. I do
> like/understand such application of parallel assignment.
>
>

Sorry, I don't understand. In this example, the rhs values are contained in
an Array, which you consider low elegance, in your first example the rhs
values are discrete. I don't see how they are the same, I think this is an
instance of your "x, y, z = [1, 2, 3]", which you consider to be low
elegance.

Re: Parallel Assignments and Elegance/Complexity Ratio.

> Sorry, I don't understand. In this example, the rhs values are contained
> in
> an Array, which you consider low elegance, in your first example the rhs
> values are discrete. I don't see how they are the same, I think this is
> an
> instance of your "x, y, z = [1, 2, 3]", which you consider to be low
> elegance.

Ah, you are right. I stand corrected. Thanks.

--
Posted via http://www.ruby-forum.com/.

 Robert Klemme 01-12-2011 08:13 AM

Re: Parallel Assignments and Elegance/Complexity Ratio.

On Tue, Jan 11, 2011 at 7:54 PM, Kedar Mhaswade
> Robert Klemme wrote in post #973945:
>> On Tue, Jan 11, 2011 at 3:29 PM, Kedar Mhaswade
>>>

>>

cecEzOg/edit?hl=3Den#
>>>
>>> Summary is, I should only spend time learning
>>> - x, y, z =3D 1, 2, 3 (# =3D> x=3D1, y=3D1, z=3D3), and
>>> - x, y =3D y, x (# =3D> swap x and y)
>>>
>>> I gather that this might be a matter of taste and style, but are other
>>> variants used by community?

>>
>> Just today I used
>>
>> =A0 def []=3D(*idx, val)
>> =A0 =A0 index_check(idx)
>> =A0 =A0 @data[idx] =3D val
>> =A0 end
>>
>> https://gist.github.com/772827
>>
>>
>> I find the pattern matching _very_ elegant. =A0This also comes in handy
>> in situations like this:

>
> Maybe I am not understanding it, but I thought that elegance is because
> of splat operator (which I am sure I like).

The splat operator is just part of the game as Josh has tried to
demonstrate. The real power comes from pattern matching which will
even work with multiple levels of nesting. And the mechanism is the
same for method and block arguments which gives you one powerful
mechanism usable in several places.

> My gripe is about various
> forms of parallel assignment and semantic/syntactic complexity because
> of that. Or are you saying that once you say you need splat operator,
> all this complexity is inevitable (of course, I can work around it by
> not using it, but then how do you all use it?)

No, I am saying that parallel assignment is just a special case of the
assignment mechanism that is also in effect for method and block
arguments. And while I don't use the fancy variants often (as Josh)
when I use it it yields an elegant solution that would be more
cumbersome without it.

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

 Colin Bartlett 01-12-2011 04:44 PM

Re: Parallel Assignments and Elegance/Complexity Ratio.

On Tue, Jan 11, 2011 at 3:30 PM, Robert Klemme
> ... I find the pattern matching _very_ elegant. ... Generally I'd say
> I don't use it overly often but when I use it it is really handy. ...

On Tue, Jan 11, 2011 at 7:58 PM, Josh Cheek <josh.cheek@gmail.com> wrote:
> I don't use it very often, but when I do, it usually makes an
> elegant solution. I think part of the reason it doesn't seem that
> way is because you are playing with it in too sterile of an environment.

That last sentence seems an accurate assessment of why it perhaps
doesn't seem much use to you, and what Josh Cheek and Robert Klemme
say about those situations where they use it seems appropriate,
although I haven't personally used it in those types of situations
because my Ruby use is relatively simple.

On Tue, Jan 11, 2011 at 8:29 PM, Kedar Mhaswade
> ... This however is already covered by me as the first case (with HIGH
> elegance rating) since you are expecting the line to contain exactly two
> strings ... and hence the (expected) number of lvalues = number of rvalues.
> I do like/understand such application of parallel assignment.

Strangely, that's the situation (number of lvalues == number of
rvalues) when I definitely dislike its use (unless it might be
necessary to ensure all the rvalues are calculated before any
assignment takes place - might side effects make this necessary?), for
two (and a quarter) reasons.

First, whenever I've benchmarked parallel assignment against
individual assignment, I've found the parallel assignment somewhat
slower. The swap is more elegant, but even that seems slower.

Second, parallel assignment can make it difficult to see what is being
assigned to what. In the following, is it instantly obvious what e is
being set to?
# a, b, c, d, e, f, g, h = q, r, s, t, u, v, w, x
If it is instantly obvious, try more l and r values and/or try longer
variable names and/or values and/or split the assignment expression
over multiple lines!

As an actual example, this from Date:
def initialize(ajd=0, of=0, sg=ITALY)
@ajd, @of, @sg = ajd, of, sg
# ...
end
Admittedly it's easy here to see what's being assigned to what, but
even so is that really better than the alternative just below? Is
there a reason for using the parallel assignment in the actual
example, other than aesthetics?
def initialize(ajd=0, of=0, sg=ITALY)
@ajd = ajd; @of = of; @sg = sg
# ...
end

The quarter is the positive side of the negative point of the second,
that explicit individual assignment is easier visually/cognitively
than parallel assignment.

*** benchmarks

require "benchmark"
a = b = c = d = e = f = g = h = nil; x = y = nil
kt = 1_000_000
Benchmark.bm( 22 ) do |bm|
bm.report( "parallel assignment" ) { kt.times{
a, b, c, d, e, f, g, h = 17, 23, 13, 48, 42, 46, 26, 24 } }
bm.report( "individual assignment" ) { kt.times{
a = 17; b = 23; c = 13; d = 48; e = 42; f = 46; g = 26; h = 24 } }
bm.report( "swap elegant" ) { kt.times{
x = 113; y = 355; x, y = y, x } }
bm.report( "swap messy" ) { kt.times{
x = 113; y = 355; z = x; x = y; y = z } }
end

Using Linux, Ruby 1.9.1, but I've had similar results on MS Windows.
user system total real
parallel assignment 0.630000 0.000000 0.630000 ( 0.635576)
individual assignment 0.400000 0.000000 0.400000 ( 0.393652)
swap elegant 0.500000 0.000000 0.500000 ( 0.506171)
swap messy 0.300000 0.000000 0.300000 ( 0.298167)

MS Windows: ruby 1.9.1p430 (2010-08-16 revision 28998) [i386-mingw32]
user system total real
parallel assignment 0.531000 0.000000 0.531000 ( 0.530000)
individual assignment 0.327000 0.000000 0.327000 ( 0.335000)
swap elegant 0.500000 0.000000 0.500000 ( 0.490000)
swap messy 0.296000 0.000000 0.296000 ( 0.300000)

MS Windows: jruby 1.5.3 (ruby 1.8.7 patchlevel 249) (2010-09-28 7ca06d7)
(Java HotSpot(TM) Client VM 1.6.0_14) [x86-java]
user system total real
parallel assignment 0.735000 0.000000 0.735000 ( 0.705000)
individual assignment 0.460000 0.000000 0.460000 ( 0.460000)
swap elegant 0.560000 0.000000 0.560000 ( 0.560000)
swap messy 0.425000 0.000000 0.425000 ( 0.425000)

Re: Parallel Assignments and Elegance/Complexity Ratio.

>> I do like/understand such application of parallel assignment.
>
> Strangely, that's the situation (number of lvalues == number of
> rvalues) when I definitely dislike its use (unless it might be
> necessary to ensure all the rvalues are calculated before any
> assignment takes place - might side effects make this necessary?), for
> two (and a quarter) reasons.

Interesting (yeah, it's a matter of taste).
But when number of variables is <=3, it looks very readable and easily
understandable.

> First, whenever I've benchmarked parallel assignment against
> individual assignment, I've found the parallel assignment somewhat
> slower. The swap is more elegant, but even that seems slower.

That's a good point, but it's judgmental based on how frequently the
code gets called.

> # a, b, c, d, e, f, g, h = q, r, s, t, u, v, w, x
> If it is instantly obvious, try more l and r values and/or try longer
> variable names and/or values and/or split the assignment expression
> over multiple lines!

Well, one can always abuse a feature. I think number of variables should
be <=3, e.g.
a, b, c = q, r ,s
@d, @e, @f = t(a), u(b), v(c) # ...

>
> As an actual example, this from Date:
> def initialize(ajd=0, of=0, sg=ITALY)
> @ajd, @of, @sg = ajd, of, sg
> # ...
> end
> Admittedly it's easy here to see what's being assigned to what, but
> even so is that really better than the alternative just below? Is
> there a reason for using the parallel assignment in the actual
> example, other than aesthetics?
> def initialize(ajd=0, of=0, sg=ITALY)
> @ajd = ajd; @of = of; @sg = sg
> # ...
> end
>
> The quarter is the positive side of the negative point of the second,
> that explicit individual assignment is easier visually/cognitively
> than parallel assignment.

Yeah. And the (others') wisdom suggests that if and when you find a
situation to apply it, it (usually) renders an elegant solution.

-Kedar

--
Posted via http://www.ruby-forum.com/.

All times are GMT. The time now is 10:15 AM.