What does the operator ||= stand for in Ruby? [duplicate] - ruby

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does ||= (or equals) mean in Ruby?
It's hard to search this in Google because it is a symbol, not text.
What does ||= stand for?
And how does it work?

It assigns a value if not already assigned. Like this:
a = nil
a ||= 1
a = 1
a ||= 2
In the first example, a will be set to 1. In the second one, a will still be 1.

From the question Common Ruby Idioms:
is equivalent to
if a == nil || a == false
a = b
end

If b is nil, assign a to it.
a = :foo
b ||= a
# b == :foo
If b is not nil, don't change it.
a = :foo
b = :bar
b ||= a
# b == :bar

This is an 'abbreviated assignment' (see Ruby Pocket Reference, page 10)
a = a || b
(meaning a is assigned the value formed by logical or of a, b
becomes
a ||= b
Almost all operators have an abbreviated version (+= *= &&= etc).

i can only guess, but i assume it stands for an logical operator combined with setting a variable (like ^=, +=, *= in other languages)
so x ||= y is the same as x = x || y
edit: i guessed right, see http://phrogz.net/ProgrammingRuby/language.html#table_18.4
x = x || y means: use x if set, otherwise assign y. it can be used to ensure variables are at least initialised (to 0, to an empty array, etc.)

Related

Why is this method not returning nil when a and b are equal?

This method should return the sum of all numbers between a and b (not ordered) and should return a or b if they are equal.
def get_sum(a,b)
a < b ? (a..b).sum : (b..a).sum
end
I'm curious as to why this method doesn't return nil when a and b are equal. The ternary operator takes care of the condition where a is less than or greater than b so I'm tempted to add return a if a==b before the ternary to test if they are equal but it appears it's not necessary.
For example, if a=5 and b=5 then the value returned is 5. Could someone explain why this is please?
This is just how the < operator (or rather method) works.
a < b
It returns true if a is less than b, otherwise it returns false.
Therefore 5 < 5 returns false.
By writing a..b you create a Range object Range.new(a, b), which starts at a and goes up to and including b. Afterwards you call the sum method on this object.
Since the range 5..5 goes from 5 to and including 5, when you convert it to an array you get:
(5..5).to_a #=> [5]
And the sum method adds all numbers which are within this range. And since this range includes a single number 5, the result of this operation is 5.
(5..5).sum #=> 5
Your method could written more concisely
def get_sum(a, b)
(a < b ? a..b : b..a).sum
end
To make it return 0 when a and b are the same, you could use a exclusive range with three dots ....
def get_sum(a, b)
(a < b ? a...b : b...a).sum
end
get_sum(5, 5) #=> 0
get_sum(5, 6) #=> 5
get_sum(5, 7) #=> 11
In fact there is no Range#sum because it is inherited from Enumerable#sum
So (5..5).sum it is the same as (5..5).to_a.sum
(5..5).to_a # => [5]
That's the reason
To fix it you can do something like this (change else branch of your ternary operator to elsif)
def get_sum(a, b)
if a < b
(a..b).sum
elsif a > b
(b..a).sum
end
end
get_sum(1, 2) # => 3
get_sum(2, 1) # => 3
get_sum(5, 5) # => nil
But in my opinion get_sum(5, 5) == 5 is more clear than nil
the ternary operator has only two branches. if the condition is not truthy the latter is executed. there are no cases where no branches are executed (unless some error happens but that's another case).
you get nil only if you have only a branch and that's not executed. e.g.
def get_sum(a,b)
if a < b
(a..b).sum
end
end
for get_sum(5, 5) this evaluates to nil

How to check if input is an integer?- Ruby [duplicate]

This question already has answers here:
How to test if a string is basically an integer in quotes using Ruby
(19 answers)
Closed 6 years ago.
I have this code where I am entering input for sides of a triangle. Depending on the values, it will print it the triangle is equilateral, isoceles, or scalene. It's executing for number values, but how do I specify that the input should only be integers? For example, if I type in "w" , it should say invalid or error, but in this case, it executes. How do I solve this?
Basically, I am looking for a way to write that if a string were to be inputted, it should show up as an error (then I would write a print statement saying it is invalid). So could I put that into another if statement? (before the ones mentioned below)
Example Code:
puts "Enter the triangle length"
x = gets.chomp
puts "Enter the triangle width"
y = gets.chomp
puts "Enter the triangle height"
z = gets.chomp
if x == y and y == z
puts "This triangle is equilateral"
else if
x==y or y == z or x==z
puts "This triangle is isoceles"
else if
x!=y and y!=z and x!=z
puts "this triangle is scalene"
end
end
end
If you are dealing with integers, you can check this with ruby.
Note, this is not as robust as regex, but it covers most cases.
if (input != '0') && (input.to_i.to_s != input.strip)
# input is not a number
else
# input is a number
end
strip is there if you want to accept input with leading or trailing whitespace, otherwise you can leave it off.
While all the other answers are probably more or less robust, I would go with another one. Since you have a triangle sides lengths, they are to be greater than zero, right? Then one might use the side effect of String#to_i method: for everything that is not converting to integer it returns zero. Therefore:
x = gets.chomp.to_i
y = gets.chomp.to_i
z = gets.chomp.to_i
raise "Wrong input" unless x > 0 && y > 0 && z > 0
# ...
You can do something like this:
x = x.to_i
puts "Please enter an integer" if x == 0
Why?
Because:
"ABC".to_i # returns 0
You may be better off calling strip instead of chomp
gets.strip.to_i
An example:
## ruby user_age.rb
# input variables
name = ""
years = 0
MONTHS_PER_YEAR = 12 # a constant
# output variable
months = 0
# processing
print "What is your name? "
name = gets.strip
print "How many years old are you? "
years = gets.strip.to_i
puts "please enter an integer" if years == 0
months = years * MONTHS_PER_YEAR
puts "#{name}, at #{years} years old, "\
"you are #{months} months old."
There are several ways of doing it. If you allow for a leading sign,
x =~ /^[+-]?\d+$/
would be a possibility.
You will also have to think whether or not you allow surrounding or embedding spaces (for instance, a space between the sign and the first digit).
I assume that any string value that, when converted to a float, equals an integer is to be accepted and the integer value is to be returned. Moreover, I assume integers can be entered with the "xen" (or "xEn") notation, where x is an integer or float and n is an integer.
def integer(str)
x = Float(str) rescue nil
return nil if x==nil || x%1 > 0
x.to_i
end
integer("3") #=> 3
integer("-3") #=> -3
integer("3.0") #=> 3
integer("3.1") #=> nil
integer("9lives") #=> nil
integer("3e2") #=> 300
integer("-3.1e4") #=> -31000
integer("-30e-1") #=> -3
integer("3.0e-1") #=> nil
You could use Integer() to check if a string contains an integer:
Integer('1234')
#=> 1234
Integer('foo')
#=> ArgumentError: invalid value for Integer()
This could be combined with a retry:
begin
number = Integer(gets) rescue retry
end

Trying to check if two strings are permutations of each other

I am trying to implement this function that checks if two strings are permutations of each other. The code itself is straightforward.
def permutation(a, b)
if a.length != b.length
return False
end
a = a.chars.sort.join
b = b.chars.sort.join
return a == b
end
a = "abcedff"
b = "acbedf"
puts (permutation(a, b).to_s)
However, when I try to run this file on the terminal, I keep getting an error that says
permutation.rb:3:in permutation': uninitialized constant False (NameError)
from permutation.rb:13:in'
I don't understand the reason for this.
Ruby is not Python. You want true and false, not True and False.
Don't over-complicate it. All you need to do is compare two character arrays. For example:
def permutation a, b
a.chars.sort == b.chars.sort
end
Given your corpus as posted, this yields:
a = "abcedff"
b = "acbedf"
permutation a, b
#=> false
permutation a, a
#=> true

Ruby -If-Else Statement (Triangle Test)

The question is Create a function that takes three numbers as input and returns true or false depending on whether those three numbers can form a triangle. Three numbers can form a triangle if the sum of any two sides is greater than the third side.
my answer is:
def is_triangle(a,b,c)
if a+b > c
return true
elsif a+c>b
return true
elsif b+c>a
return true
else
return false
end
end
the thing is: my supposed false return keeps returning true. please help!
This logic should work for finding your triangle
def is_triangle?(a,b,c)
sorted = [a,b,c].sort
greatest_side = sorted.pop
greatest_side < sorted.sum
end
Yet another approach:
def is_triangle?(a,b,c)
[a,b,c].max < [a,b,c].sum/2.0
end
Or for Ruby outside of Rails:
def is_triangle?(a,b,c)
[a,b,c].max < [a,b,c].inject(:+)/2.0
end
Your problem is that unless all 3 numbers are 0 one of your ifs will always be true. What you want is something more like
def is_triangle(a,b,c)
a + b > c && a + c > b && b + c > a
end
is_triangle(3,6,8) #=> true
is_triangle(3,6,80) #=> false
Nothing you pass into this is going to return false. Your method is wrong.
You can tell if three sides make a triangle by finding the longest side and then adding the remaining two sides. If they are greater than the longest side, then the sides can make a traingle.
I suggest if you are sure your logic is correct change the method to
def is_triangle?(a, b, c)
a+b > c or b+c > a or c+a > b
end
But according to me it is not so the method should be
def is_triangle?(a, b, c)
a+b>c and b+c>a and c+a>b
end
Some points to note about ruby conventions:
Method which returns boolean ends with '?'
A ruby method returns the last evaluated expression, so writing return is redundant here.
puts " enter a, b and c values"
a=gets.to_i
b=gets.to_i
c=gets.to_i
if a+b > c
print true
elsif a+c>b
print true
elsif b+c>a
print true
else
print false
end
you can also use this code too.. this is much easy
This is also a solution to this problem :
if 2*[a,b,c].max < a + b + c
true
else
false
end
There are a variety of different inequalities theoretically possible:
http://en.wikipedia.org/wiki/Triangle_inequality

(Ruby) how to return a -1 specifically for a specific comparison

let's say when I'm comparing values in ruby, i have a value in mind that no matter what I want, using sort on that value and anything else returns a -1 (so this value is default sorted as smaller than everything).
for example, let's say i want '100' to sort smaller 100% of the time against 99. so that if i'm sorting values in an array, and a comparison comes up between 100 and 99, 100 is sorted smaller (ie, -1 is returned). but, i want all the other cases to be normal (98 is smaller than 99, 50 is bigger than 30, etc)
edit: okay this is what i want
if i have an x and a y, i do not want to use
x <=> y
i want to use (in pseudocode and hand-wavy-ness)
x > y
which means, this x is always greater than this y
Why don't you instead use a dictionary to keep values associated with their relative value? In this case, the string abc can be mapped to -1, and then just make sure no other values map to values equal to or less than -1.
Edit: If you're only concerned with one particular value breaking the norm, then this solution is not for you.
Easier to handle the specialness outside of the sort!
module Enumerable
def sort_excluding(*vals)
special,rest = partition {|x| vals.include?(x)}
rest.sort + special
end
end
One way to do it would be to implement a derivative class for your custom comparisons (http://www.ruby-doc.org/core/classes/Comparable.html)
Here's some sample code (and tests) for you:
class StrA < String
include Comparable
attr :str
def <=>(anOther)
if (str == "abc" && anOther.str == "abc")
0
elsif (str == "abc")
-1
elsif (anOther.str == "abc")
1
else
str <=> anOther.str
end
end
def initialize(str)
#str = str
end
def inspect
#str
end
end
And the tests:
a = StrA.new("Z")
b = StrA.new("B")
c = StrA.new("abc")
d = StrA.new("")
a > b # 1
a > c # 1
c > a # -1
d > c # 1
c > d # -1
c < d # 1
c > d # -1
[a, b, c, d].sort! # [ "Z", "B", "", "abc"]
I think what you want is:
[30, 50, 4, 0, 100, -22, 99].sort_by {|a| [a == 100 ? -1 : 0, a ]}.reverse
which gives:
99
50
30
4
0
-22
100
Hope I understood the question!
Array#sort or Enumerable#sort(I don't know what you are trying to sort) can take an obtional block. If the block is given, the block is used for comparison instead of <=>
For example this code will sort reversed:
foo.sort { |a,b| b <=> a }
In your case you need to call #sort something like the following:
foo.sort do |a,b|
if [a,b].sort == [99,100]
b-a # returns 1 or -1 so that 99 > 100
else
a <=> b
end
end
I am not entirely sure about the way you are trying to sort, but this should enable you to use sort in the manner you need. More inforamtion about Array#sort, and any other method can be found on your linux(and possible other OS's) via ri like this: ri Array#sort.
You could override the sort method for the object like so:
class Fixnum
alias old_sort <=>
def <=>(object)
if (self == 100 or object == 100)
return -1
else
return self.old_sort object
end
end
end
puts (101 <=> 100)
Edit 1: Above is a fully working example.
Edit 2: As stated by johannes in the comments, you really shouldn't implement this exact code. This is merely a simple example of how to override your sorting method to do domain-specific logic. Also, updated the code to reflect johannes' other comment about comparing with both object and self.

Resources