Boolean behaving strange, logic issue - ruby

I'm having an issue with boolean expected behavior. I can make the desired result but I'm not sure why the strange behavior occurs. This produces a wrong result:
def palindrome?(string)
rev = string.reverse
if rev == string
true
else
false
end
end
palindrome?("abc") == false # => true
palindrome?("abcba") == true # => true
palindrome?("z") == true # => true
while this produces the correct result:
def palindrome?(string)
rev = string.reverse
if rev == string
true
end
end
palindrome?("abc") == false # => false
palindrome?("abcba") == true # => true
palindrome?("z") == true # => true
The following might be similar:
def nearby_az(string)
counter = 0
string = string.split("")
if string.count == 1
false
elsif (string.index("z") - string.index("a")) <= 3
true
else
false
end
end
nearby_az("a") == false # => true
nearby_az("z") == false # => true

Because Ruby method will return the result of executing the last expression. For this if statement:
if rev == string
true
end
it will return nil if rev != string.

Actually, your first function is producing the correct result. The problem is the second formula. It returns nothing if the string is not a palindrome; it only returns true if the string is a palindrome. Therefore,
palindrome?("abc") == false
for the second equation returns false because palindrome?("abc") returns nothing because "abc" is not a palindrome, and so it is not equal to false.

The second implementation of palindrome? will return nil if it is not a palindrome. Thus nil == false # => false. In Ruby, all functions return something although it may be nil.

Related

Ruby unless vs if logic

Can someone please explain this behavior? Why does nil return true when result = true if i but returns false when result = false unless i
unless case screenshot
def my_all?(pattern = nil)
result = true
my_each do |i|
case pattern
when nil
p result, i
result = false unless i
when Regexp
result = false unless i.to_s.match(pattern)
when Class
result = false unless i.is_a?(pattern)
when String, Numeric
result = false unless i == pattern
end
result = yield(i) if block_given? && pattern.nil?
break if !result
end
result
end
if case screenshot
def my_all?(pattern = nil)
result = false
my_each do |i|
case pattern
when nil
p result, i
result = true if i
when Regexp
result = true if i.to_s.match(pattern)
when Class
result = true if i.is_a?(pattern)
when String, Numeric
result = true if i == pattern
end
result = yield(i) if block_given? && pattern.nil?
break if !result
end
result
end
In your second example, once result is set to true, nothing ever sets it to false again. So if the first value yielded from my_each is truthy, then the my_all? method will return true.
This second example seems like more like an implementation of any?, rather than all?. Except it is actually only checking the first element. If the first i is falsey, then the loop will be broken and it will return false. If the first i is truthy, then it will be set to true and nothing will set it back to false, and the method will return `true.
See these two examples, where the only difference is the values yielded by my_each:
Returns true (first value is truthy)
Returns false (first value is falsey)

Write a function to return "true" or "false" if passed "1"/"0" or "true"/"false"

I need a simple function to return "true" or "false" the argument passed to it is:
1 or 0, or true or false
I currently had something like this, so the answer, if possible, should be concise as per below:
def boolean(value); return value ? ( value == 1 ? "true" : "false) : nil; end
Thanks.
Some ideas:
def boolean(x)
%w{1 true}.include?(x).to_s
end
def boolean(x)
(x == '1' || x == 'true').to_s
end
There's also the wannabe bool gem:
require 'wannabe_bool'
'1'.to_b # => true
'0'.to_b # => false
'true'.to_b # => true
'false'.to_b # => false
You might want to have a look how Rails does this typecasting in its database connection adapter:
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
# convert something to a boolean
def value_to_boolean(value)
if value.is_a?(String) && value.empty?
nil
else
TRUE_VALUES.include?(value)
end
end
See: docs for ActiveRecord::ConnectionAdapters::Column
I think this'll work, comments are welcome though:
def b(k); return k ? ( (k == "1" || k == "true") ? "true" : "false" ) : nil; end
puts b("1")
puts b("0")
puts b("true")
puts b("false")
Result:
true
false
true
false

Need clarification on Ruby logical operators

Recently I started learning Ruby. I am practicing logical operators in irb and I got these results, which I don't understand. Can you please clarify these examples for me?
1 and 0
#=> 0
0 and 1
#=> 1
0 && 1
#=> 1
As opposed to other languages like C, in Ruby all values except for nil and false are considered “truthy”. This means, that all these values behave like true in the context of a boolean expression.
Ruby's boolean operators will not return true or false. Instead, they return the first operand that causes the evaluation of the condition to be complete (also known as short-circuit evaluation). For boolean and that means, it will either return the first “falsy” operand or the last one:
false && 1 # => false (falsy)
nil && 1 # => nil (falsy)
false && nil # => false (falsy)
1 && 2 # => 2 (truthy)
For boolean or that means, it will either return the first “truthy” operand or the last one:
false || 1 # => 1 (truthy)
nil || 1 # => 1 (truthy)
false || nil # => nil (falsy)
1 || 2 # => 1 (truthy)
This allows for some interesting constructs. It is a very common pattern to use || to set default values, for example:
def hello(name)
name = name || 'generic humanoid'
puts "Hello, #{name}!"
end
hello(nil) # Hello, generic humanoid!
hello('Bob') # Hello, Bob!
Another similar way to acheive the same thing is
name || (name = 'generic humanoid')
With the added benefit that if name is truthy, no assignment is performed at all. There is even a shortcut for this assignment of default values:
name ||= 'generic humanoid'
If you paid careful attention you will have noticed that this may cause some trouble, if one valid value is false:
destroy_humans = nil
destroy_humans ||= true
destroy_humans
#=> true
destroy_humans = false
destroy_humans ||= true
destroy_humans
#=> true, OMG run!
This is rarely the desired effect. So if you know that the values can only be a String or nil, using || and ||= is fine. If the variable can be false, you have to be more verbose:
destroy_humans = nil
destroy_humans = true if destroy_humans.nil?
destroy_humans
#=> true
destroy_humans = false
destroy_humans = true if destroy_humans.nil?
destroy_humans
#=> false, extinction of humanity digressed!
That was close! But wait, there is another caveat – specifically with the usage of and and or. These should never be used for boolean expressions, because they have very low operator precedence. That means they will be evaluated last. Consider the following examples:
is_human = true
is_zombie = false
destroy_human = is_human && is_zombie
destroy_human
#=> false
is_human = true
is_zombie = false
destroy_human = is_human and is_zombie
destroy_human
#=> true, Waaaah but I'm not a zombie!
Let me add some parentheses to clarify what's happening here:
destroy_human = is_human && is_zombie
# equivalent to
destroy_human = (is_human && is_zombie)
destroy_human = is_human and is_zombie
# equivalent to
(destroy_human = is_human) and is_zombie
So and and or are really just useful as “control-flow operators”, for example:
join_roboparty or fail 'forever alone :('
# this will raise a RuntimeError when join_roboparty returns a falsy value
join_roboparty and puts 'robotz party hard :)'
# this will only output the message if join_roboparty returns a truthy value
I hope that clarifies everything you need to know about these operators. It takes a bit of getting used to, because it differs from the way other languages handle it. But once you know how to use the different options, you've got some powerful tools at hand.
Both values are 'truthy' (in Ruby everything that isn't nil or false is truthy), so in all cases the second value is returned. On the contrary, if you use 'or', first value will be returned:
1 || 0 #=> 1
0 || 1 #=> 0
In Ruby both 0 and 1 is truth value. (Only nil and false are false value)
If both operands are truth value, and, && returns the last value.

Ruby - return false unless a value is true in which case return the value

If a = false and b = 2 is there a concise way to accomplish this? Using just return a unless b returns 'nil' instead of '2'.
I have
def checkit
return a unless b
b
end
Will this statement call b twice?
A real life case for this is:
def current_user
#current_user ||= authenticate_user
end
def authenticate_user
head :unauthorized unless authenticate_from_cookie
end
def authenticate_from_cookie
.
if user && secure_compare(user.cookie, cookie)
return user
else
return nil
end
end
Try this:
( b == true ) ? a : false
where a is a value you need to return
I do not know why you have false stored in the variable a, so I omitted that. As I understand, you want to pass a value to the method checkit, which should return the value if its boolean value is true (which means everything except values nil and false), and otherwise return the value. In that case, just use this:
def checkit(value)
value || false
end
checkit(1) # => 1
checkit(false) # => false
checkit('value') # => "value"
checkit(nil) # => false

How can I check to see if a pair of words are only 1 letter different?

In writing a method to compare 2 words, how can I check to see if the words are only 1 letter different? I'm assuming words are same length and order of letters doesnt matter (see "cobra","bravo").
def one_letter_apart?(word1, word2)
I expect the results below:
one_letter_apart?("abra","abro") == true
one_letter_apart?("cobra","bravo") == true
one_letter_apart?("bravo","tabby") == false
one_letter_apart?("abc","cab") == false
I have tried a few ways of manipulating them (splitting,sorting,then setting equal and adding to new array, then counting), but none so far have worked. Any ideas are greatly appreciated.
This one makes use of the fact that String#sub substitutes only the first thing it finds.
def one_different_char?(str, other)
other_str = other.dup
str.chars{|char| other_str.sub!(char, '')} #sub! just replaces just one occurence of char
other_str.size == 1
end
test_set = [["abra","abro"],["cobra","bravo"],["bravo","tabby"],["abc","cab"]]
test_set.each{|first, second| puts one_different_char?(first, second) }
#true
#true
#false
#false
Check Levenshtein Distance
You want the Levenstein distance. For example, using the text gem:
require 'text'
def one_letter_apart? string1, string2
Text::Levenshtein.distance(string1, string2).eql? 1
end
one_letter_apart? "abra", "abro"
# => true
one_letter_apart? "cobra", "bravo"
# => false
def one_letter_apart?(s1, s2)
return false if s1.length != s2.length
a2 = s2.chars.to_a
s1.chars.each do |c|
if i = a2.index(c)
a2.delete_at(i)
end
end
a2.length == 1
end
one_letter_apart?("abra","abro") == true
# => true
one_letter_apart?("cobra","bravo") == true
# => true
one_letter_apart?("bravo","tabby") == false
# => true
one_letter_apart?("abc","cab") == false
# => true
Update: To answer your question of how it works: This is the exact same general algorithm as steenslag's, but I didn't think of using String#sub! to do the removal, so I converted to arrays and used a combination of index and delete_at to remove the first occurrence of the given character. The naïve approach is a2.delete_at(a2.index(c)), but if the character c doesn't exist in a2, then index returns nil, which is an invalid input for delete_at. The workaround is to only call delete_at if index returns something non-nil, which is what I've done. i is declared and set to a2.index(c), and the value of that assignment is evaluated by if. It's the same as:
i = a2.index(c)
if i
# ...
I much prefer steenslag's approach and would have done the exact same thing if I'd thought of String#sub!.
This function returns true if two strings have equal lengths and only one different letter while all the other letters are in the same positions:
def one_letter_apart? string1, string2
return false if string1.size != string2.size
found = false
(0...string1.size).each do |i|
next if string1[i] == string1[i]
return false if found # if found is already true, and we found another difference, then result is false.
found = true # We found the first difference.
end
found # True if only one difference was found.
end
This function handles letters in wrong positions (like "cobra" and "bravo") as well:
def one_letter_apart? string1, string2
letters1 = string1.chars.each_with_object(Hash.new(0)) { |c, h| h[c] += 1 }
letters2 = string2.chars.each_with_object(Hash.new(0)) { |c, h| h[c] -= 1 }
diff = letters1.merge(letters2) { |key, count1, count2| count1 + count2 }
return diff.values.select { |v| v != 0 } .sort == [-1, 1]
end

Resources