I recently came across this magical operator when digging into Groovy: <=>
Groovy has really made me happy with elvis operators ?. and ?: which I use constantly now and very much wish were in Java. With this new operator, I have only found this reference. It seems to make comparators much easier. My question is how does it handle null values and how does it compare non Comparable object. Does this operator have a name, I couldn't find it Googling.
You got a list of operators here. It is called the "Spaceship" operator. It handles null without problem.
It's called the spaceship operator and is also commonly used for comparison in Ruby.
http://www.objectpartners.com/2010/02/08/the-groovy-spaceship-operator-explained/
Name : Spaceship operator
Method that it uses : a.compareTo(b) //where a and b are the variables that has been used
Class : java.lang.Comparable
And this link explains about that operator in a bit more . Click Here
Like many others mention, it's called the spaceship operator. Here's my test:
def a
def b
println 1 <=> 0 // 1
println 0 <=> 1 // -1
println 1 <=> a // 1
println b <=> 0 // -1
println a <=> b // 0
println "abc" <=> "def" // -1
println "abc" <=> 1 // throw exception: java.lang.ClassCastException
Related
I'm reading this book Well-Grounded Rubyist and its Control-Flow Techniques chapter on p.184 has a simple example of implementing map with an iterator:
class Array
def my_map
c=0
acc = []
until c == size
acc << yield self[c]
c += 1 end
acc
end
end
I have the following error when run this code ruby MY_FILE.rb:
MY_FILE.rb:6: syntax error, unexpected `self', expecting `end'
acc << yield self[c]
Solution to this problem is to put brackets around yield self[c], so this whole line would look like this:
acc << (yield self[c])
Then, the routine works with this one-line change. In my understanding the problem is either with operator precedence or with order of evaluation. I surfed the web for quite a while but could not pinpoint why exactly the code from the book does not work.
Why the book example does not work? What is operator precedence or/and order of evaluation in acc << yield self[c]?
Usually, method parameters come in parentheseis:
x=foo(y)
a=bar(baz(z))
b=7+baz(w)
In certain situations (i.e. if precedences does not bind otherwise), you can leave out the braces. Therefore,
x = foo y
works, but
a = bar baz z
is interpreted as
a = (bar(baz))(z)
and
b = 7 + baz w
as
b = (7+baz) w
This is risky, if the interpretation of the expression results in something meaningful. In this case, you would not even get an error message, but the program would simply behave in a different way than you expected.
In general, it is a good idea to always use parenthesis when invoking methods.
Is there a better (ruby way) to achieve the following:
if a.nil?
a = 1
else
a += 1
end
If a can ever only be nil or an integer, then
a = a.to_i + 1
a = (a || 0) + 1 also works .
Be More Idiomatic
If you know that a will never be true, false, or something else that can't be coerced to an Integer, you can use this Ruby idiom:
a ||= a.to_i.succ
Be More Robust
A more robust construct might be:
a = a.respond_to?(:succ) ? a.succ : 1
It's not as pretty, but it will be more likely to "do the right thing" in your case. You could also ask if a.is_a? Integer, but that fails to take advantage of duck typing in the event that you have an object that is incrementable but isn't really an Integer.
There's always more than one way to do something like this, so your mileage may vary.
a = a ? a+1 : 1
...............
I have an inject call
[2,4,6].inject(true) { |res, val| res && val % 2 == 0 }
and want to send the && operator to inject as in inject(0, :+). How can I do that?
You can't because && and ||, unlike other operators, are not syntacic sugar for methods (i.e. there is no method called && or ||), so you can't reference them using a symbol.
However you can avoid using inject to compute the logical conjunction or disjunction of an array of boolean values, replacing it with all? or any? respectively, because for any array the following conditions hold:
ary.inject(true) { |res, b| res && b } == ary.all?
ary.inject(false) { |res, b| res || b } == ary.any?
So, for example, the code you posted could be rewritten as:
[2,4,6].map(&:even?).all?
# => true
Update: obviously my latter example is not the right way to express this computation, falsetru's answer is much faster:
require 'fruity'
compare(
-> { (0..1000).map(&:even?).all? },
-> { (0..1000).all?(&:even?) }
)
Running each test 1024 times. Test will take about 2 seconds.
Code 2 is faster than Code 1 by 111x ± 10.0
How about using Enumerable#all?
[2,4,6].all? &:even?
# => true
[2,4,6,5].all? &:even?
# => false
If you want to use inject, you need to define an instance method.
class Object
def is_even(val)
self && val % 2 == 0
end
end
[2,4,6].inject(true, :is_even) # => true
[1,2,4,6,5].inject(true, :is_even) # => false
'&&' is not a method, hence you can't inject it. However you can inject & method.
[2,4,6,5].map(&:even?).inject(true, :&)
Which will do the same
NOTE: This however should not be done, as it is extremely risky and might cause unexpected consequences (if run on collection containing at least one non-boolean (true, false, nil) value). You should always use any? or all? methods instead.
inject(0, :+) would add all the elements of the array, regardless of what the content of the elements are (odd, even, etc).
If you want to inject(true, &:&&) (I know this doesn't work), your array should be an array of boolean values for your question to make sense, which would be the same as: [true, false].all?
important:
you can't pass both block arg and actual block which means you can't check if it's even inject an && at the same time.
If you insist, try this out:
[2,4,6].inject(true, & lambda { |x,y| x && y })
=> 6
This is the equivalent of what you're asking for (which I still don't totally understand)
In Ruby Integer === 5 returns true. Similarly String === "karthik" returns true.
However, 5 === Integer returns false. And "karthik" === String.
Why is the operator not commutative?
The simple answer is: because it doesn't make sense. The relationship the operator describes is not commutative, why should the operator be?
Just look at your own examples: 5 is an Integer. But is Integer a 5? What does that even mean?
=== is the case subsumption operator, and subsumption doesn't commute.
The fact that the case subsumption operator uses equals signs, and that it is usually called the triple equals, threequals or case equality operator is terribly unfortunate since it not only has absolutely nothing to do with equality, but it also does not conform to many of the laws that equality conforms to, such as transitivity and as you mentioned commutativity.
For more of my ranting about === see
What does the === operator do in Ruby?
=== vs. == in Ruby
How does Integer === 3 work?
One very simple reason is that the is_a? relationship for classes just can't be commutative. Consider the case where both operands are classes:
Class === String
This will return true because String.is_a?(Class). However String === Class will return false, because Class.is_a?(String) is false and that is of course as it should be.
Another reason is that the semantics of === depends on its left operand. This has again two reasons: a) In ruby the semantics always depend on the left operand, because the left operand is the receiver of the method call and b) it is useful, so you can use e.g. classes, ranges and regexen in a case statement with the intended semantics.
Many operators are not commutative.
The === is called the "case equality operator" because it is called when branching is a case.
It is nice and useful that:
foo = 42
case foo
when Integer
# branches here
when String
# etc...
end
It would not be very useful if
foo = Integer
case foo
when 42
# would branch here??
when 666
# etc...
end
Note that in Ruby 1.9, the === operator on a Proc/lambda will call that Proc:
divisible_by_three = ->(x){x % 3 == 0}
divisible_by_three === 42 # => true
Again, very useful in a case statement, but not much in the reverse order.
it needs to implement case-when comparisons
It's normal to have non-commutative operators.
/ - % [] . -> ^ << >> < <= > >= && || = += -= ,
And as it happens, === exists in part as the case-when operator. That's rather elaborate in Ruby, and it couldn't be so if it had to be simplified to a commutative op.
Is there a ruby idiom for "If do-this," and "do-this" just as a simple command?
for example, I'm currently doing
object.method ? a.action : nil
to leave the else clause empty, but I feel like there's probably a more idiomatic way of doing this that doesn't involve having to specify a nil at the end. (and alternatively, I feel like taking up multiple lines of code would be wasteful in this case.
As a general rule: you pretty much never need the ternary operator in Ruby. The reason why you need it in C, is because in C if is a statement, so if you want to return a value you have to use the ternary operator, which is an expression.
In Ruby, everything is an expression, there are no statements, which makes the ternary operator pretty much superfluous. You can always replace
cond ? then_branch : else_branch
with
if cond then then_branch else else_branch end
So, in your example:
object.method ? a.action : nil
is equivalent to
if object.method then a.action end
which as #Greg Campbell points out is in turn equivalent to the trailing if modifier form
a.action if object.method
Also, since the boolean operators in Ruby not just return true or false, but the value of the last evaluated expression, you can use them for control flow. This is an idiom imported from Perl, and would look like this:
object.method and a.action
a.action if object.method?
Greg's answer is the best, but for the record, and even more than in C, expressions and statements are equivalent in Ruby, so besides a.action if o.m? you can also do things like:
object.method? && a.action
You can write (a; b; c) if d or even
(a
b
c
) if d
or for that matter: (x; y; z) ? (a; b c) : (d; e; f)
There is no situation in Ruby where only a single statement or expression is allowed...
result = (<expression> && <true value>) || <false value>
value = 1
result = (value == 1 && 'one' ) || 'two'
result #=> 'one'
Explain: value == 1 && 'one' #=> returns last expression result, value is equals 1 so and section will be evaluated, and return 'one'.
value = 0
result = (value == 1 && 'one' ) || 'two'
result #=> 'two'
Explain: value != 1 and 'and' expression will not be evaluated, but instad will be used 'or' expression and it returns 'two'
Another way this can be done on the same line is:
if object.method; a.action end
This is considered bad style by Rubocop because it uses a semicolon to terminate the expression, but I find it more readable in some conditions than tacking on the if statement at the end. It is easier to overlook an if statement at the end and I don't always want to return something if the condition isn't true(as you are forced into with a ternary operator).
You can also be a bit more verbose and rubocop friendly:
if object.method then a.action end