Ruby If and Unless conditional explanation - ruby

I want to ask for a little explanation about if and unless, I know the two of this is an opposite of each other. All of this time I have used both without no problem until now I find something weird. Let's check my code :
ausysdir = AUDIO_SYSTEM_FOLDER_NAME
Dir.mkdir(ausysdir) if ausysdir != "" && !File.directory?(ausysdir) # Number 1
Dir.mkdir(ausysdir) unless ausysdir == "" && File.directory?(ausysdir) # Number 2
Now in the directory I work the folder ausdyr is already exist.
But from the code above the the number one conditional which is if not return an error(this is what I expected, so great), But not with number 2 conditional which is unless, when I think that if and unless is an opposite, then my syntax above should be correct, am I right? Or my syntax is wrong? Or Unless have a different behaviour? Or do I missed something here?
Thank you very much.
EDIT : When I mean error, is an exception because the file is already exist.

unless expr is the same as if !expr. But !(a && b) is not !a && !b as you seem to think, it is actually !a || !b (this is one of De Morgan's laws BTW). So your if and unless statements are not at all equivalent.
Build the truth table if you don't believe me. Then study some Propositional Calculus (which every programmer should be familiar with) if you don't believe your truth table.
If your if works then you're unless would be:
unless ausysdir == "" || File.directory?(ausysdir)

TL;DR Don't use unless with more than one condition. Most people have to think too hard to understand it, and then have about a 50/50 chance of getting it right.
Here is a test of two booleans using if/unless and &&/or:
def test(first, second)
puts "#{first}, #{second}"
puts " ...passes if #{first} && #{second}" if first && second
puts " ...passes unless #{first} && #{second}" unless first && second
puts " ...passes if #{first} || #{second}" if first || second
puts " ...passes unless #{first} || #{second}" unless first || second
end
test(true, true)
test(true, false)
test(false, true)
test(false, false)
The results:
true && true
...passes if true && true
...passes if true || true
true && false
...passes unless true && false
...passes if true || false
false && true
...passes unless false && true
...passes if false || true
false && false
...passes unless false && false
...passes unless false || false
Your first condition boils down to if false && false, according to the false, false results above, if false && false doesn't pass. Reaction: Pretty straightforward and easy to see.
Your second condition boils down to unless false && true, according to the false, true results above, unless false && true does pass. Reaction: That seems like it possibly might make sense if I think about it some more.

Related

Ruby variable not read in loop

Ruby newbie getting even more confused.
If I remove the discountgiven == false from the 4th line, code in the condition works.
If I add it back, the code doesn't work.
What am I doing wrong?
Is it something wrong in the discountgiven variable scope?
discountgiven = false
Input.cart.line_items.each do |item|
product = item.variant.product
if product.tags.include? 'device-ePod' && discountgiven == false
discount_to_apply = item.line_price * (1.0)
item.change_line_price(item.line_price - discount_to_apply, {message: 'Free Epod'})
discountGiven = true
end
end
use parentheses in conditions:
Change :
if product.tags.include? 'device-ePod' && discountgiven == false
to
if product.tags.include?('device-ePod') && discountgiven == false
Your former logic is being evaluated as if product.tags.include?('device-ePod' && discountgiven == false)
Also, what spickermann suggested, you probably typo in discountGiven. It should be discountgiven.

simple ruby unless method mistake

I am very new to ruby. This might sound very naive to you but I can't understand what is happening here
matched_criteria = match_criteria(c1, c2)
puts "answer is #{matched_criteria}"
def match_criteria(crit_val1, crit_val2)
if crit_val1.present?
puts "present"
else
puts "absent"
end
return true unless crit_val1.present? || crit_val2.present?
return false unless crit_val1.present? && crit_val2.present?
end
Output:
absent
answer is false
please explain
def match_criteria(crit_val1, crit_val2)
# Check if #1 is present? or blank?
if crit_val1.present?
puts "present"
else
puts "absent"
end
# Return true unless EITHER (||) are present
# ...or you could think of it like...
# Return true if BOTH (&&) are blank
return true unless crit_val1.present? || crit_val2.present?
# Return false unless BOTH are present
# ..or...
# Return false if EITHER is blank
return false unless crit_val1.present? && crit_val2.present?
end
This is actually a Ruby on Rails question. blank? and present? are instance methods only available in Rails, but not in the standard Ruby library.
Whenever you call match_criteria with one parameter that is a non-blank value, the condition
crit_val1.present? || crit_val2.present?
will be true, but since you are negating it the first return statement does not get called.
In that same situation the second conditional evaluates to false, and since you're negating it the second return successfully returns false.
Your problem has nothing to do with Ruby (or Rails) per se. It has to do with misunderstanding the logical operators involved, which of course would be a problem in any language.
Consider this:
def test(x, y)
return true unless x || y
false unless x && y
end
p test(true, false) # => false
p test(true, true) # => nil
p test(false, false) # => true
In the first test (x = true, y = false) the first condition fails because x || y is true (it would succeed only if x || y evaluated to false, since you're using unless instead of if). The second condition succeeds because x && y is not true. So, the method returns false.
In the second test, neither condition succeeds, because both x || y and x && y evaluate to true. (Again, since you are using unless instead of if, a condition can only succeed if it evaluates to false.) So, the method returns nil.
In the third test, the first condition succeeds, because x || y evaluates to false. So, the method returns false.
It's most unlikely that you want these logical conditions. In the first place, you probably don't want a set of logical conditions that doesn't cover all the possibilities. Also, I don't see a reason for using unless instead of if; it seems like you're needlessly complicating your logic.
Of course, you haven't explained exactly what logical conditions you're looking for, so we're all playing "blind mechanic" here to some extent. But my feeling is that you are attempting to check whether your crit_val1 or crit_val2 are present, and return false if either of them is. If so, all you would have to do is this:
def match_criteria(crit_val1, crit_val2)
# other stuff
return false if crit_val1.present? || crit_val2.present?
true
end

Conditioning tri-state logic

I have a variable that takes a value out of tri-state booleans:
asap = true
asap = false
asap = nil
Ruby's conditionals interpret them as truthy or falsey. Especially, nil is not distinguished from false.
asap = nil
unless asap
puts 'False'
end
# => 'False'
A statement that distinguishes false from nil is:
if asap == false
puts 'False'
end
but RubyMine 6.3.3 complains that the expression could be simplified, and suggests unless asap. Is there a different (right) way to do the conditioning?
TLDNR;
if FalseClass === asap
puts 'False'
end
Tri-state usually suggests that there are three possible branches, which forces me to think that neither if nor ternary ? is generally applicable.
puts case asap
when FalseClass then 'False'
when TrueClass then 'True'
when NilClass then 'Nil'
else 'You are kidding on me, aren’t you?'
end
Hope it helps.
UPD: The more I think about it, the more I am inclined to think that ternary is fine as well:
puts asap.nil? ? 'Nil' : asap ? 'True' : 'False'
UPD2: Actually, the tri-equal comparision operator, which is transparently used in case and which apparently evaluates to true for false === false and true === true might satisfy IDEA aka RubyMine as well:
if asap === false
puts 'False'
end
UPD3: As #sawa mentioned in the comment, IDEA is wrong here since nil == false evaluates to false and therefore == false is not generally equal to unless.

ruby conditions using && and ||

I can't figure how to correctly write the rule
I want express the following rule:
If conclusion is 'negative' one of the premises must be negative.
Here's how I tried to write this.
def test4b
if (#conclusion.getQuality == 'negative' && (#major.getQuality != 'negative' || #minor.getQuality != 'negative'))
validity = "invalid (4b) Negative conclusion without a negative premise"
else
validity = "pass"
end
end
But this isn't working. It seems to exclude every syllogism with a negative premise. Again, I only want to exclude syllogisms where which have a negative conclusion without any negative premises.
It should be:
if (#conclusion.getQuality == 'negative' && (#major.getQuality != 'negative' && #minor.getQuality != 'negative'))
Both of #major and #minor are not to be 'negative'.
Your method is to retrun the logical value of !conclusion => (!major || !minor). The negation of this expression would be:
!conclusion && !(!major || !minor) <=> !conclusion && major && major
(DeMorgan's law). This is the condition youa re lloking for, note there are no || here.
Also note that it would be more readable though if you do:
if (#conclusion.getQuality == 'negative' && [#major, #minor].all? {|m| m.getQuality != 'negative'})
or
if (#conclusion.getQuality == 'negative' && ![#major, #minor].any? {|m| m.getQuality == 'negative'})

Ruby: Clean code for checking nil /false conditional statement?

I always meet this Ruby problem, I want to write it more cleanly.
var a can be nil
a.value can also be nil
a.value has possible true or false value
if (not a.nil?) && (not a.value.nil?) && a.value == false
puts "a value is not available"
else
puts "a value is true"
end
The problem is that the conditional statement is too clumsy and hard to read.
How can I improve the checking nil and false conditional statement?
Thanks, I am a Ruby newbie
Ruby on rails has an extension called try which allows you to write:
if a.try(:value) == false
which is very clean. Without try, you can just write
if a && a.value == false
If a.value is nil, it is not false, so that is ok :)
If it is possible that a.value is not defined (which would raise an exception), I would write that as follows:
if a && a.respond_to?(:value) && a.value == false
[UPDATE: after ruby 2.3]
Since ruby 2.3 there is an even shorter version:
if a&.value == false
which is almost equivalent to a.try(:value) (but is pure ruby). Differences:
if value does not exist, the &. operator will throw, try will just return nil (preferable or not?)(note: try! would also throw).
when cascading try or &. they also handle false differently. This follows logically from previous difference, try will return nil, while &. will throw because false knows no methods :P
You can achieve it in more compacted way using Safe Navigation Operator (&.):
if a&.value == false
Source : http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
if a && a.value!=false
puts "a value is true"
else
puts "a value is not available"
end
or just
puts a && a.value!=false ? "a value is true" : "a value is not available"
The simplest and cleanest way is to flip it and reverse it. Check for the truthy value rather than the falsey values
if a && a.value
puts "a value is true"
else
puts "a value is not available"
end
Of course in Rails you could do it either way by using blank? or present?
Your condition is redundant. If a.value is to be false, then it would not be nil.
if a.nil?.! && a.value == false
puts "a value is not available"
else
puts "a value is true"
end
This will always return a boolean, if nil it will return false; if false it returns false; if true returns true. Try it out, it's nice and short:
!!a.try(:value) == false

Resources