shortcut for add unless nil ?

I

Iain Barnett

Hi,

Is was wondering if there's a Ruby idiom for adding variables that =
should be arrays but that may be nil, for example:

[1] + (a || [ ]) + (b || [ ]) + (c || [ ])

but that's a bit ugly IMO. I've had a look, but didn't see anything. I =
know I could use a fold or something similar, but I was wondering if =
there was something in the realms of ||=3D (i.e. nice, idiomatic =
shortcut) for this kind of thing?


Iain=
 
X

Xavier Noria

You could do something like this (untested):

[a, b, c, d].compact.reduce:)+)

though whether it is more clear is debatable. I think it reads fine if you'r=
e fluent.

Sent from my iPhone

Hi,
=20
Is was wondering if there's a Ruby idiom for adding variables that should b=
e arrays but that may be nil, for example:
=20
[1] + (a || [ ]) + (b || [ ]) + (c || [ ])
=20
but that's a bit ugly IMO. I've had a look, but didn't see anything. I kno=
w I could use a fold or something similar, but I was wondering if there was s=
omething in the realms of ||=3D (i.e. nice, idiomatic shortcut) for this kin=
d of thing?
 
R

Ryan Davis

Hi,
=20
Is was wondering if there's a Ruby idiom for adding variables that =
should be arrays but that may be nil, for example:
=20
[1] + (a || [ ]) + (b || [ ]) + (c || [ ])
=20
but that's a bit ugly IMO. I've had a look, but didn't see anything. I =
know I could use a fold or something similar, but I was wondering if =
there was something in the realms of ||=3D (i.e. nice, idiomatic =
shortcut) for this kind of thing?

The most idiomatic (and performant) shortcut is to not have the =
situation arise in the first place. Nil checks everywhere is generally a =
design smell. Push things up to whereever the defaults should be figured =
out... in railsy parlance:

thingies =3D params[:thingies] || []

then later you never have to worry about it.
 
I

Iain Barnett

The most idiomatic (and performant) shortcut is to not have the =
situation arise in the first place. Nil checks everywhere is generally a =
design smell. Push things up to whereever the defaults should be figured =
out... in railsy parlance:
=20
thingies =3D params[:thingies] || []
=20
then later you never have to worry about it.
=20

I agree to an extent, but even when you're defining defaults it would be =
nice:

thingies =3D params[:thingies] || []
wotsits =3D params[: wotsits] || []
stuff =3D thingies + wotsits

isn't as nice as
=20
stuff =3D params[:thingies] +? params[:wotsits]

where +? is my new infix operator of choice for "add unless nil" :) =
Sometimes I don't want the extra typing or to use extra locals that will =
only be used for that one line.


Iain=
 
I

Iain Barnett

You could do something like this (untested):
=20
[a, b, c, d].compact.reduce:)+)
=20
though whether it is more clear is debatable. I think it reads fine if =
you're fluent.

That works, thanks, though it starts to feel a bit messy if the array =
members aren't short vars and/or have square brackets too, as it gets =
pushed over several lines, and personally I dislike wrapping things with =
square brackets only to take them off just to use a fold. Whereas:

a +
[b,c,d] +
e +
f=20

would read a lot better in that kind of situation. If it could be done =
:) The short vars in my example were due to typing laziness, I was =
thinking more for stuff like this:

HEADERS =3D ENV["BLAH_BLAH"] + ENV["BLAH_BLAH_BLAH_BLAH"]

which begins to look unwieldy even with just two fairly long vars:
=20
HEADERS =3D [ENV["BLAH_BLAH"] + =
ENV["BLAH_BLAH_BLAH"]].compact.reduce:)+)

but

HEADERS =3D ENV["BLAH_BLAH"] +=20
ENV["BLAH_BLAH_BLAH"] +
ENV["BLAH_BLAH_BLAH_BLAH"]

will remain clear even as the vars stack up.

Much appreciated though.

Iain=
 
R

Robert Klemme

You could do something like this (untested):

=A0 =A0[a, b, c, d].compact.reduce:)+)

though whether it is more clear is debatable. I think it reads fine if y=
ou're fluent.

That works, thanks, though it starts to feel a bit messy if the array mem=
bers aren't short vars and/or have square brackets too, as it gets pushed o=
ver several lines, and personally I dislike wrapping things with square bra=
ckets only to take them off just to use a fold. Whereas:
=A0 =A0a +
=A0 =A0[b,c,d] +
=A0 =A0e +
=A0 =A0f

would read a lot better in that kind of situation. If it could be done :)=
The short vars in my example were due to typing laziness, I was thinking m=
ore for stuff like this:
=A0 =A0HEADERS =3D ENV["BLAH_BLAH"] + ENV["BLAH_BLAH_BLAH_BLAH"]

which begins to look unwieldy even with just two fairly long vars:

=A0 =A0HEADERS =3D [ENV["BLAH_BLAH"] + ENV["BLAH_BLAH_BLAH"]].compact.red= uce:)+)

but

=A0 =A0HEADERS =3D ENV["BLAH_BLAH"] =A0 =A0 =A0 =A0 =A0 +
=A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH"] =A0 =A0 =A0+
=A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH_BLAH"]

will remain clear even as the vars stack up.

Much appreciated though.

What about

res =3D [a,b,c].inject([1]) {|r,x| x and r.concat(x) or r}

or

res =3D [a,b,c].inject([1]) {|r,x| x ? r.concat(x) : r}

or

res =3D [1]
[a,b,c].each {|x| x and res.concat x}

Kind regards

robert

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

Colin Bartlett

What about
...
res = [1]
[a,b,c].each {|x| x and res.concat x}

I'd also wondered whether Iain Barnett really needed array + array2
rather than array.concat(array2), and wondered about something
similar:

class Array
def concat_not_nil( *args )
args.each { |arg| concat(arg) unless arg.nil? }
self
end
def plus_not_nil( *args )
new_ary = self.dup
new_ary.concat_not_nil( *args )#
end
end
 
I

Iain Barnett

What about
...
res =3D [1]
[a,b,c].each {|x| x and res.concat x}
=20
I'd also wondered whether Iain Barnett really needed array + array2
rather than array.concat(array2), and wondered about something
similar:
=20
class Array
def concat_not_nil( *args )
args.each { |arg| concat(arg) unless arg.nil? }
self
end
def plus_not_nil( *args )
new_ary =3D self.dup
new_ary.concat_not_nil( *args )#
end
end
=20

I really appreciate the responses, but if I apply them to the code that =
provoked the question for me, and my dislike of wrapping things in an =
array only to get access to a function or block (which also then removes =
the wrapper), and of creating locals for a single use, then I'm not so =
sure they work. As in, there are so many great shortcuts in Ruby that =
feel very natural too, that some kind of infix operator works well for =
args split over several lines due to their length.

I think this reads very easily, HEADERS is 3 env vars added up into one:

HEADERS =3D ENV["BLAH_BLAH"] +=20
ENV["BLAH_BLAH_BLAH"] +
ENV["BLAH_BLAH_BLAH_BLAH"]

to this, which creates an array then destroys it, which kind of masks =
the intention:

HEADERS =3D [
ENV["BLAH_BLAH"],
ENV["BLAH_BLAH_BLAH"],
ENV["BLAH_BLAH_BLAH_BLAH"]
].inject([1]) {|r,x| x and r.concat(x) or r}

If I was going to use a fold or something similar then I would probably =
have done something like this, or used a ternary operator in a fold, =
(knowing the way I think):

HEADERS =3D [
ENV["BLAH_BLAH"],
ENV["BLAH_BLAH_BLAH"],
ENV["BLAH_BLAH_BLAH_BLAH"]
].reject{|x| x.nil? }.reduce:)+)

but compared to this it seems a bit creaky:

HEADERS =3D ENV["BLAH_BLAH"] +?=20
ENV["BLAH_BLAH_BLAH"] +?
ENV["BLAH_BLAH_BLAH_BLAH"]

Is there a way to define infix operators in Ruby, as I quite like this =
one? :)

Actually, looking at the code they'd be ENV["BLAH_BLAH"].split(":") as =
they were PATHs, but I'm not sure the inclusion adds anything to the =
examples.

Regards,
Iain=
 
R

Robert Klemme

What about
...
res =3D [1]
[a,b,c].each {|x| x and res.concat x}

I'd also wondered whether Iain Barnett really needed array + array2
rather than array.concat(array2), and wondered about something
similar:

class Array
=A0def concat_not_nil( *args )
=A0 =A0args.each { |arg| concat(arg) unless arg.nil? }
=A0 =A0self
=A0end
=A0def plus_not_nil( *args )
=A0 =A0new_ary =3D self.dup
=A0 =A0new_ary.concat_not_nil( *args )#
=A0end
end

I really appreciate the responses, but if I apply them to the code that p=
rovoked the question for me, and my dislike of wrapping things in an array =
only to get access to a function or block (which also then removes the wrap=
per), and of creating locals for a single use, then I'm not so sure they wo=
rk.

Maybe you should reduce your requirements...
As in, there are so many great shortcuts in Ruby that feel very natural t=
oo, that some kind of infix operator works well for args split over several=
lines due to their length.
I think this reads very easily, HEADERS is 3 env vars added up into one:

=A0 =A0HEADERS =3D ENV["BLAH_BLAH"] =A0 =A0 =A0 =A0 =A0 +
=A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH"] =A0 =A0 =A0+
=A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH_BLAH"]

to this, which creates an array then destroys it, which kind of masks the= intention:

=A0 =A0HEADERS =3D [
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH"],
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH"],
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH_BLAH"]
=A0 =A0 =A0 =A0 =A0 =A0 =A0].inject([1]) {|r,x| x and r.concat(x) or r}

If I was going to use a fold or something similar then I would probably h=
ave done something like this, or used a ternary operator in a fold, (knowin=
g the way I think):
=A0 =A0HEADERS =3D [
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH"],
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH"],
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH_BLAH"]
=A0 =A0 =A0 =A0 =A0 =A0 =A0].reject{|x| x.nil? }.reduce:)+)

but compared to this it seems a bit creaky:

=A0 =A0HEADERS =3D ENV["BLAH_BLAH"] =A0 =A0 =A0 =A0 =A0 +?
=A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH"] =A0 =A0 =A0+?
=A0 =A0 =A0 =A0 =A0 =A0 =A0ENV["BLAH_BLAH_BLAH_BLAH"]

Is there a way to define infix operators in Ruby, as I quite like this on=
e? :)

There is no way to define new operators other than hacking the
interpreter. IMHO it's a bad idea to do this just for a minor
nuisance.
Actually, looking at the code they'd be ENV["BLAH_BLAH"].split(":") as th=
ey were PATHs, but I'm not sure the inclusion adds anything to the examples=
 
I

Iain Barnett

this one? :)
=20
There is no way to define new operators other than hacking the
interpreter. IMHO it's a bad idea to do this just for a minor
nuisance.

Well, yeah, I won't be hacking the interpreter for this :) Just =
wondering. Is there somewhere I could suggest `+?` as an operator for =
Ruby 2? It was only an off the cuff remark but I quite like it :)
=20
I don't understand what's wrong with
=20
HEADERS =3D []
%w{BLAH BLAH_BLAH BLAHHH}.each do |var|
x =3D ENV[var] and HEADERS.concat(x.split(/:+/))
end
=20
Nice short concise and yet readable.
=20

I totally agree, there's nothing wrong with that, and out of the =
suggestions given I think that's the one I like best (which makes me =
feel better for labouring the point:) It's only that, as I said, Ruby =
usually has some really nice shortcuts that don't seem like shortcuts, =
just natural looking. A bit like having to define getters and setters, =
there's nothing wrong with it, but `attr_accessor :var` is really nice, =
as is `||=3D` and it's ilk.

Wouldn't this be quite nice, for instance?

[a,b,c,d].reduce:)+?)

That's all.


Regards,
Iain=
 
S

serialhex

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

...personally i think it would be nice to be able to define new operators
(like the aforementioned +?) that way when a situation like this comes up,
one can simply:

op_def +? # maybe? i'm assuming it'd have special syntax...
# stuff
end

and get on with life, making your code cleaner & simpler. IDK how well this
would work or its potential ramifications, but i think it fits into the
'flavor' of ruby to be able to do such things (i mean, you can define/change
damn near *everything else* in the language, why not operators?)
hex



one? :)

There is no way to define new operators other than hacking the
interpreter. IMHO it's a bad idea to do this just for a minor
nuisance.

Well, yeah, I won't be hacking the interpreter for this :) Just wondering.
Is there somewhere I could suggest `+?` as an operator for Ruby 2? It was
only an off the cuff remark but I quite like it :)
I don't understand what's wrong with

HEADERS = []
%w{BLAH BLAH_BLAH BLAHHH}.each do |var|
x = ENV[var] and HEADERS.concat(x.split(/:+/))
end

Nice short concise and yet readable.

I totally agree, there's nothing wrong with that, and out of the
suggestions given I think that's the one I like best (which makes me feel
better for labouring the point:) It's only that, as I said, Ruby usually has
some really nice shortcuts that don't seem like shortcuts, just natural
looking. A bit like having to define getters and setters, there's nothing
wrong with it, but `attr_accessor :var` is really nice, as is `||=` and it's
ilk.

Wouldn't this be quite nice, for instance?

[a,b,c,d].reduce:)+?)

That's all.


Regards,
Iain
 
R

Ryan Davis

=20
On 24 Mar 2011, at 22:45, Ryan Davis wrote:
=20
situation arise in the first place. Nil checks everywhere is generally a =
design smell. Push things up to whereever the defaults should be figured =
out... in railsy parlance:
=20
thingies =3D params[:thingies] || []
=20
then later you never have to worry about it.
=20
=20
I agree to an extent, but even when you're defining defaults it would = be nice:
=20
thingies =3D params[:thingies] || []
wotsits =3D params[: wotsits] || []
stuff =3D thingies + wotsits
=20
isn't as nice as
=20
stuff =3D params[:thingies] +? params[:wotsits]
=20
where +? is my new infix operator of choice for "add unless nil" :) =
Sometimes I don't want the extra typing or to use extra locals that will =
only be used for that one line.

Yeah. Well... I disagree. We really don't need extra magical syntax. I =
can almost guarantee you that this proposal would get rejected by =
ruby-core@, but you're free to ask.
 
I

Iain Barnett

...personally i think it would be nice to be able to define new = operators
(like the aforementioned +?) that way when a situation like this comes = up,
one can simply:
=20
op_def +? # maybe? i'm assuming it'd have special syntax...
# stuff
end
=20
and get on with life, making your code cleaner & simpler. IDK how = well this
would work or its potential ramifications, but i think it fits into = the
'flavor' of ruby to be able to do such things (i mean, you can = define/change
damn near *everything else* in the language, why not operators?)
hex

I agree. In Haskell, for instance, you can make a prefix function into =
an infix by surrounding it with backticks, e.g.

plus 1 2

becomes

1 `plus` 2

Something similar would be helpful on occasion, IMO.


=20
Yeah. Well... I disagree. We really don't need extra magical syntax. I =
can almost guarantee you that this proposal would get rejected by =
ruby-core@, but you're free to ask.

I find that a strange argument, applied across the language and a lot of =
what is regularly used would be binned. Aliases, attributes and method =
synonyms would all go immediately.

Regards,
Iain
 
R

Ryan Davis

I can almost guarantee you that this proposal would get rejected by =
ruby-core@, but you're free to ask.
=20
I find that a strange argument, applied across the language and a lot =
of what is regularly used would be binned. Aliases, attributes and =
method synonyms would all go immediately.

Well I'm not applying across the whole language... Otherwise we'd all be =
coding in assembly. :D
 
R

Robert Klemme

..personally i think it would be nice to be able to define new operators
(like the aforementioned +?) that way when a situation like this comes up,
one can simply:

op_def +? # maybe? i'm assuming it'd have special syntax...
# stuff
end

and get on with life, making your code cleaner& simpler. IDK how well this
would work or its potential ramifications, but i think it fits into the
'flavor' of ruby to be able to do such things (i mean, you can define/change
damn near *everything else* in the language, why not operators?)

Because that would completely break parsing. Whenever an operator
definition would be seen the whole source would need reparsing - or at
least part of it. It would be tricky to determine which part. Think of
situations like this:

def foo
x +? y
end

op_def +?(a,b)
p a, b
a + b
end

Now, Ruby would have bailed out with a syntax error already before it
could read the new operator definition. If you defer error reporting to
a later point in time then execution would become much more complex
because parsing would have to occur chunk wise. And the whole thing
will likely get slower as well.

If you try something like prescanning all sources for operator
definitions before parsing the code then this is not possible because
you can easily construct 'require' and 'load' statements which do not
have a String constant argument but whose argument is constructed at
runtime. So now you have a chicken egg issue: you need to parse the
code in order to be able to execute it but you also need to execute the
code in order to be able to parse it...

And then of course there are issues of readability. In general I'd say,
too much flexibility can rather harm than do good. It's the right
balance which is most productive. And IMHO Matz has done a wonderful
job at this.

Kind regards

robert
 
R

Robert Klemme

I agree. In Haskell, for instance, you can make a prefix function
into an infix by surrounding it with backticks, e.g.

plus 1 2

becomes

1 `plus` 2

Something similar would be helpful on occasion, IMO.

But that's something different than defining a new operator. It's just
fixed syntax for a method call. That approach doesn't suffer from the
problems I laid out in my other reply to this thread.

Kind regards

robert
 
A

Aaron D. Gifford

An alternative for the original poster's ENV variables potentially
containing colon-separated paths:

%w{FOO BAR BAZ}.map{|x| ENV[x].to_s}.join(':').split(/:+/)

The .to_s should handle nil just fine. Concatenate all the strings,
then do the split.

Aaron out.
 
A

Aaron D. Gifford

I was heard to muse:
An alternative for the original poster's ENV variables potentially
containing colon-separated paths:

=A0%w{FOO BAR BAZ}.map{|x| ENV[x].to_s}.join(':').split(/:+/)

The .to_s should handle nil just fine. Concatenate all the strings,
then do the split.

Append a .uniq to that as desired. Or do:
%w{FOO BAR BAZ}.map{|x| ENV[x].to_s.split(/:+/) || []}.flatten.uniq

Aaron out.
 
A

Aaron D. Gifford

I've found that whenever I have a variable that may be nil, doing
to_i or .to_s or .to_a or something similar (so long as the NilClass
supports the type conversion I need) can come in handy.

So for something like:

big_array = array_or_nil_var1 + array_or_nil_var2 + array_or_nil_var3

I would just do:

big_array = array_or_nil_var1.to_a + array_or_nil_var2.to_a +
array_or_nil_var3.to_a

Aaron out.
 
R

Robert Klemme

I'm not so sure about that. =A0If it were just possible to specify that a
method need not be connected to its object with the "dot", it would give
the appearance of an infix operator for those who really want it, but
would only conform to the standard rules for method calls.

That's a different story because that is a fixed syntax for method
calls and *not* introducing arbitrary new operators. Introducing new
operators ad hoc changes the syntax hence you need to reparse or do
parsing completely different as I have laid out.
=A0The parser
would just have to maintain a last-object context of one to determine
whether what follows is a method to which the last object responds.

It wouldn't work though because of ambiguity. You cannot change "a.b
c" to "a b c" which has a valid interpretation already today:

$ ruby19 -e 'def a(*x)printf("a:%p\n",x)end;def
b(*x)printf("b:%p\n",x)end;def c(*x)printf("c:%p\n",x)end;a b c'
c:[]
b:[nil]
a:[nil]

$ ruby19 -e 'def a(*x)printf("a:%p\n",x)end;def
b(*x)printf("b:%p\n",x)end;def c(*x)printf("c:%p\n",x)end;a.b c'
a:[]
c:[]
-e:1:in `<main>': private method `b' called for nil:NilClass (NoMethodError=
)

Even simpler "a.b" means "evaluate a and invoke b on the result" while
"a b" means "evaluate b and use it as argument when calling method a".
Of course, I'm not sure that would be so great a task to dump on the
interpreter, but I don't think it would necessarily be as dire as you
describe, as long as we're willing to recognize that infix arithmetic,
comparison, and assignment operators are actually methods.

Many operators are actually methods already. (And 1.9 has expanded on
this IIRC.) But that is not the problem here, the problem lies with
the syntax. What makes operators special is not the fact that they
use a specific subset of ASCII but rather the role they play in the
syntax of the language.
Frankly, I'd be happy without infix operator syntax (with its precedence
rules) at all. =A02.plus(3) is conceptually simpler than 2 + 3 in the
context of code; so is something like (+ 2 3), add(2,3,), or sum 2 3.

2.plus 3 looks pretty "infix"y to me anyway, but it's still a method call
with obvious meaning and precedence.

Yes, but the downside is that there is only one level of precedence.
The good thing about using operators in syntax is that you can have
precedence without explicit bracketing thus repeating something that
most people know from mathematics. Everybody with basic math
knowledge knows what "a + b * c" means and in what order it is
evaluated. For methods you need to do "a.plus(b.mult c)". ("times"
was already taken.) Of course we could have Ruby with the same
functionality as today without *any* operators without restriction in
functionality. But then code becomes more verbose and less elegant
and concise which is one of the strengths of the language. I am not
sure that you would be really that happy with the result - certainly I
wouldn't. :)

Kind regards

robert


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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top