When you use || and &&, you may think that you are playing with true/false values. For instance, a condition that uses || or && will give you back true or false.
I found out recently that this is sometimes true (and sometimes not)! Now, what am I talking about? I always thought that if you evaluate (true || true), the expression evaluates to true because at least one of them is true. Well, apparently, it does evaluate to true, but not for the reason I had thought.
In Ruby, when you evaluate (true || true), what it is actually doing is evaluating the first condition. If it is truthy (ie. not false or nil) you get the first condition back. If it is falsy (ie. false or nil), you get the second condition back. So the following occurs:
('blah' || true) #=> 'blah' - not true!
(false || 'blah') #=> 'blah' again!
For &&, if the first condition is falsy, you get the first condition back. If it is truthy, you get the second condition back. Check it out:
(true && 'blah') #=> 'blah'
(nil && true) #=> nil - not false!
Using || and && still work in 'if' statements as expected, but it doesn't work the way as first thought. With this new insight, you can use || and && for shortcuts.
Instead of:
if options[:product].nil?
product = Product.new
else
product = options[:product]
end
You could do:
product = options[:product] || Product.new
And instead of:
unless product.nil?
name = product.name
end
You coud do:
name = product && product.name
Obviously, use these shorthands with caution, as it can convolute readability for other readers. On another note, JavaScript also follows this evaluation. Have fun!
W
Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts
Sunday, November 16, 2008
Saturday, October 18, 2008
Ruby: Time made easy
Here's another example of how Ruby just makes things simple. Imagine you're working with time and you have to figure out the difference between 2 times and whether they fall within a certain range. In most languages, you have to figure out how many seconds exist in your time range. For example, 1 minute would have 60 seconds and 1 day would have 86,400 seconds. That's not too hard to figure out, but imagine the readability!
t2 - t1 < 86400
That just begs the question, "Where does 86400 come from?". Imagine you had to explicitly put down the number for 1 week?
Well, Ruby is awesome in that it concentrates on readability. Since everything in Ruby is an object, numbers are just Fixnum (or Bignum). Either way, that means we can use certain methods associated with the object. Check this out:
t2 - t1 < 1.second
t2 - t1 < 1.minute
t2 - t1 < 1.hour
t2 - t1 < 1.day
t2 - t1 < 1.week
t2 - t1 < 1.month
t2 - t1 < 1.year
Now how readable is that?
W
t2 - t1 < 86400
That just begs the question, "Where does 86400 come from?". Imagine you had to explicitly put down the number for 1 week?
Well, Ruby is awesome in that it concentrates on readability. Since everything in Ruby is an object, numbers are just Fixnum (or Bignum). Either way, that means we can use certain methods associated with the object. Check this out:
t2 - t1 < 1.second
t2 - t1 < 1.minute
t2 - t1 < 1.hour
t2 - t1 < 1.day
t2 - t1 < 1.week
t2 - t1 < 1.month
t2 - t1 < 1.year
Now how readable is that?
W
Tuesday, September 30, 2008
initialize + super
Here's a word of caution to those using inheritance and calling super in the initialize method. Consider this:
def initialize(name, price)
super
@name = name
@price = price
end
Which parent method does it call? I was surprised to find out that without () after super, it automatically calls the initialize in the parent that has the parameters (name, price), rather than the one without parameters. If your intention was to call the parent's initialize without parameters, than you must do:
super()
W
def initialize(name, price)
super
@name = name
@price = price
end
Which parent method does it call? I was surprised to find out that without () after super, it automatically calls the initialize in the parent that has the parameters (name, price), rather than the one without parameters. If your intention was to call the parent's initialize without parameters, than you must do:
super()
W
Tuesday, September 23, 2008
Stack and Queue Implementation
Array in Ruby already has built in push and pop function you can use, but if you want to make it more strict and idiot proof, here's how you can implement it.
class Stack
def initialize
@stack = []
end
def push(x)
@stack.push(x)
end
def pop
@stack.pop unless is_empty?
end
def peek
@stack.last
end
def is_empty?
@stack.empty?
end
end
class Queue
def initialize
@queue = []
end
def enqueue(x)
@queue << x
end
def dequeue(x)
@queue.shift
end
def peek
@queue.first
end
def is_empty?
@queue.empty?
end
end
J
class Stack
def initialize
@stack = []
end
def push(x)
@stack.push(x)
end
def pop
@stack.pop unless is_empty?
end
def peek
@stack.last
end
def is_empty?
@stack.empty?
end
end
class Queue
def initialize
@queue = []
end
def enqueue(x)
@queue << x
end
def dequeue(x)
@queue.shift
end
def peek
@queue.first
end
def is_empty?
@queue.empty?
end
end
J
Fun with String
This is why Ruby is amazing :D
1. converting strings to numbers
"123".to_i => 123
Duh, everyone knows that
but how bout
"123stfu".to_i => 123
or
" 123stfu ".to_i => 123
you can even indicate the base of your conversion
"111".to_i(2) => 7 # treat it as a binary number
"111".to_i(8) => 73 # treat it as an octal
split a string and convert it to an interger
"123 456 789".split.map {|m| m.to_i} => [123, 456, 789]
2. Replacing text
we can implement the lame rot13 encryption scheme in one line :)
class String; def rot13; self.tr("A-Ma-mN-Zn-z", "N-Zn-zA-Ma-m");end;end
"Please hide this line".rot13 => "Cyrnfr uvqr guvf yvar"
3. Counting Characters
s1 = "abcabcabcde"
s1.count("c") => 3
s1.count("abc") => 9
s1.count(s1) => 11
count can handle negation
s1.count("^abc") => 2
s1.count("^a") => 8
and range of chars
s1.count("a-c") => 9
s1.count("^a-z") => 0
4. Reversing a String
Microsoft loves to ask interviewee to reverse the word order of a String during an interview, it takes around 15 - 20 lines to implement the function in Java or C++, but we can do it in one :)
phrase = "This is a joke"
phrase.split.reverse.join(" ") => "joke a is This"
so next time you goto an interview, asks if they'll let you do it in Ruby ;)
5. Removing Duplicate Characters
string = "aaaaaaaaaaaaaaaaaaa".squeeze => "a"
string = "aaaaaaaaaaaaaaaaaaB".squeeze => "aB"
string = "hehe....".squeeze(".") => "hehe."
J
1. converting strings to numbers
"123".to_i => 123
Duh, everyone knows that
but how bout
"123stfu".to_i => 123
or
" 123stfu ".to_i => 123
you can even indicate the base of your conversion
"111".to_i(2) => 7 # treat it as a binary number
"111".to_i(8) => 73 # treat it as an octal
split a string and convert it to an interger
"123 456 789".split.map {|m| m.to_i} => [123, 456, 789]
2. Replacing text
we can implement the lame rot13 encryption scheme in one line :)
class String; def rot13; self.tr("A-Ma-mN-Zn-z", "N-Zn-zA-Ma-m");end;end
"Please hide this line".rot13 => "Cyrnfr uvqr guvf yvar"
3. Counting Characters
s1 = "abcabcabcde"
s1.count("c") => 3
s1.count("abc") => 9
s1.count(s1) => 11
count can handle negation
s1.count("^abc") => 2
s1.count("^a") => 8
and range of chars
s1.count("a-c") => 9
s1.count("^a-z") => 0
4. Reversing a String
Microsoft loves to ask interviewee to reverse the word order of a String during an interview, it takes around 15 - 20 lines to implement the function in Java or C++, but we can do it in one :)
phrase = "This is a joke"
phrase.split.reverse.join(" ") => "joke a is This"
so next time you goto an interview, asks if they'll let you do it in Ruby ;)
5. Removing Duplicate Characters
string = "aaaaaaaaaaaaaaaaaaa".squeeze => "a"
string = "aaaaaaaaaaaaaaaaaaB".squeeze => "aB"
string = "hehe....".squeeze(".") => "hehe."
J
List of reflective methods
I find the following commands very useful from time to time, but I kept forgetting their names. So I'm creating this post to help remind myself.
a.class
# returns the class of an object a
a.superclass
# returns the superclass of an object a
a.instance_of? A
# determines if a.class == A
a.is_a? A
#determines whether a is an instance of c, or of any of its subclass, or of any modules.
a.respond_to? :something
#determines if a has a public or protected method with the specified name.
J
a.class
# returns the class of an object a
a.superclass
# returns the superclass of an object a
a.instance_of? A
# determines if a.class == A
a.is_a? A
#determines whether a is an instance of c, or of any of its subclass, or of any modules.
a.respond_to? :something
#determines if a has a public or protected method with the specified name.
J
Monday, September 22, 2008
Duck Typing and Type Checking
Consider the following example:
class A
attr_accessor :x
def initialize
@x = 0
end
def +(other)
a = A.new
a.x = @x + other.x
return a
end
end
class B
attr_accessor :x
def initialize
@x = 0
end
end
a = A.new
a.x = 10
b = B.new
b.x = 5
puts (a+b).x # this will return 15
Ruby is a very dynamic language, the addition function doesn't care if the argument passed in is of type A, as long as it looks and behaves like A. This is called duck typing, name after the phase "if it walks like a duck and quacks like a duck, it must be a duck!"
if you want to ensure the object passed into the + function has to have an x accessor, you can add this to def +(other)
raise "error" unless other.respond_to? :x
or if you want to ensure object passed into the + function has to be of class A, you can add this instead:
raise "error" unless other.is_a? A
J
class A
attr_accessor :x
def initialize
@x = 0
end
def +(other)
a = A.new
a.x = @x + other.x
return a
end
end
class B
attr_accessor :x
def initialize
@x = 0
end
end
a = A.new
a.x = 10
b = B.new
b.x = 5
puts (a+b).x # this will return 15
Ruby is a very dynamic language, the addition function doesn't care if the argument passed in is of type A, as long as it looks and behaves like A. This is called duck typing, name after the phase "if it walks like a duck and quacks like a duck, it must be a duck!"
if you want to ensure the object passed into the + function has to have an x accessor, you can add this to def +(other)
raise "error" unless other.respond_to? :x
or if you want to ensure object passed into the + function has to be of class A, you can add this instead:
raise "error" unless other.is_a? A
J
Instance Variable
Java and C++ programmers are used to declaring their variables in a class, and they think they should do the same in Ruby. WRONG!
For example:
class Animal
@age = 0
@name = ""
def initialize(age, name)
@age = age
@name = name
end
end
In this example, two sets of @age, @name are created. When the initialize method is called, self holds an instance of the Animal class. But the code outside of that function are executed as part of the definition of the Animal class. When @age and @name are executed, it refers to the Animal class, not the object instance. Hence @age, @name inside the initialize function are different from the @age, @name on the outside.
J
For example:
class Animal
@age = 0
@name = ""
def initialize(age, name)
@age = age
@name = name
end
end
In this example, two sets of @age, @name are created. When the initialize method is called, self holds an instance of the Animal class. But the code outside of that function are executed as part of the definition of the Animal class. When @age and @name are executed, it refers to the Animal class, not the object instance. Hence @age, @name inside the initialize function are different from the @age, @name on the outside.
J
Sunday, September 21, 2008
Variable Parameters
Sometimes we want a method that can take any number of parameters.
One way to do it is to pass in a hash, but this can be annoying. Another method is to put an * before the method's parameter. Within the body of the method, list will be treated as an array with 0 or more elements.
For example:
def average(*list)
x = 0
list.each {|l| x+= l}
l/list.size
end
irb>average 1,2,3,4,5,6,7,8,9
=> 5
J
One way to do it is to pass in a hash, but this can be annoying. Another method is to put an * before the method's parameter. Within the body of the method, list will be treated as an array with 0 or more elements.
For example:
def average(*list)
x = 0
list.each {|l| x+= l}
l/list.size
end
irb>average 1,2,3,4,5,6,7,8,9
=> 5
J
Copying Objects
When you use =, clone, or dup to copy an object, only the object reference is copied,
not the referenced object itself (Shallow Copy).
In order to do a deep object copy, we can use the marshaling trick by dumping and
loading the object.
For example:
def deepcopy(something)
Marshal.load(Marshal.dump(something))
end
irb>b = "abcd"
=> "abcd"
irb>a = b
=> "abcd"
irb>a.upcase!
=>"ABCD"
irb>b
=>"ABCD" # see how b changes too
irb>b = deepcopy(a)
=>"ABCD"
irb>b.downcase!
=>"abcd"
irb>a
=>"ABCD" # a stayed the same this time
J
not the referenced object itself (Shallow Copy).
In order to do a deep object copy, we can use the marshaling trick by dumping and
loading the object.
For example:
def deepcopy(something)
Marshal.load(Marshal.dump(something))
end
irb>b = "abcd"
=> "abcd"
irb>a = b
=> "abcd"
irb>a.upcase!
=>"ABCD"
irb>b
=>"ABCD" # see how b changes too
irb>b = deepcopy(a)
=>"ABCD"
irb>b.downcase!
=>"abcd"
irb>a
=>"ABCD" # a stayed the same this time
J
Saturday, September 20, 2008
More on enumerable
In ruby, I use .each alot to visit every elements in the array.
Did you know there's a .step(x) function that allows you to visit
every x th element in the array?
For example:
r = 0..3
r.each {|l| prints l.to_s + ' ' } # Prints 0 1 2 3
r.step(2) {|l| prints l.to_s + ' '} # Prints 0 2
J
Did you know there's a .step(x) function that allows you to visit
every x th element in the array?
For example:
r = 0..3
r.each {|l| prints l.to_s + ' ' } # Prints 0 1 2 3
r.step(2) {|l| prints l.to_s + ' '} # Prints 0 2
J
Thousands Seperator
In ruby, underscore can be inserted into integer literals as thousands separator.
For example:
1_000 will be interpreted as 1000
1_000 + 1_000 will equal 2000
1_000_000_000 will be interpreted as 1 billion
the same technique works for float number as well
For example:
1_000.1 will be interpreted as 1000.1
J
For example:
1_000 will be interpreted as 1000
1_000 + 1_000 will equal 2000
1_000_000_000 will be interpreted as 1 billion
the same technique works for float number as well
For example:
1_000.1 will be interpreted as 1000.1
J
Subscribe to:
Posts (Atom)