Jump to content






8.times versus for i in 1..8

Posted by oichidan, 18 May 2017 · 77 views

rgss3 looping
I've recently came across a code excerpt that piqued my interest.
# Taken from Ace Equip Engine, Yanfly Engine Ace
# All credits and copyrights where they due.
8.times {|i| draw_item(0, line_height * i, i) }
The syntax above essentially repeats the instructions in the block given after the times method call as much as 8 times. If we define this code using a for loop, an equivalent would be:
for i in 0..8 do
  draw_item(0, line_height * i, i)
end
Then a question arose on my mind. Which would be better and why?

Let's consider the pros and cons of both code structures.

First one is the times function. This function actually calls for an iterator block, requiring evaluation, as it defines a yield clause in its definition. It basically repeats the instructions in the iterator block, yielding a local variable to signal how many times the iteration had performed, as much as n times, where n is the said number. 8.times would mean 8 times.

A consideration would be if the number is not a number literal, but is a variable that contains a numeric value. Fine, we can use to_i to convert non-integer to integer values, but what if the variable ends up being a negative number?
exa = 0
exa -= rand <= 0.5 ? 1 : -1
# ...
# at this point, exa would contain either positive or negative number, and we want to perform a loop
# of instruction for exa times.
exa.to_i.times do |ex|
  puts ex + "\n"
end
The RPG Maker VX Ace help files mention that if the instance variable that calls the times function is a negative number, it does nothing. It doesn't matter if we mean to check for negative variables, since we can simply just exa.abs.to_i.times do |ex| things out, but if we meant to do a loop (say, if exa is 2, then we want a loop 2 times, as do if exa is -2, and using abs function is tedious in your opinion), in such cases, the for loop is handy.
exa = 0
exa -= rand <= 0.5 ? 1 : -1
# blablabla
for ex in 0..exa do
  puts ex + "\n"
end 
This way, whether exa is positive or negative, the loop will still perform its job.

The downside of a for loop is that while iterator expressions introduces a new block scope for local variables (and thus, we can perform wider range of actions inside the block scope), for loops doesn't introduce such scopes, and so, for has no effects on local variables.

Basically, we would say that the following two loops will output the same.
8.times do { |x| puts x }
for x in 0..8 do puts x end
So, we can conclude that both of them are equivalent in terms of performance. If we want to perform operations that modify the local variable passed to the loop, then the times and iterator block expression would come handy. Otherwise, the for loop might come in handy as well. The usage of both of them mutually is a choice of each own programmer.




There is somethings you kind of failed to mention. Like the .each method. For example, you can write 'for i in 0..8 do' as '(0..8).each do |i|' if you wanted. Why would you want to? Because really 'for' is kind of 'syntactic sugar', it isn't really native Ruby but a cheat for people used to programing in other languages. And .each is only a basic loop and can be swapped out for all sorts of other functions like .find or .any? or .select or so on.

What I'd like to know is the origin of ! meaning NOT.

 

!= means NOT EQUAL TO in Ruby, but I am curious as to why =/= is not just as usable.

There is somethings you kind of failed to mention. Like the .each method. For example, you can write 'for i in 0..8 do' as '(0..8).each do |i|' if you wanted. Why would you want to? Because really 'for' is kind of 'syntactic sugar', it isn't really native Ruby but a cheat for people used to programing in other languages. And .each is only a basic loop and can be swapped out for all sorts of other functions like .find or .any? or .select or so on.

 

Dang, how could  I've forgotten? Thank you for mentioning it.

 

And... I've just known that 'for' isn't native Ruby. I thought it is.

 

As far as I know, the other functions in the Enumerable module all uses the .each function, too. So yes, I guess that's why functions like .any? and .find can be swapped out with .each.

What I'd like to know is the origin of ! meaning NOT.

 

!= means NOT EQUAL TO in Ruby, but I am curious as to why =/= is not just as usable.

 

Hmmm... Many languages play the ! operator as a negation operator, i.e. !true = false and !false = true.

 

Visual Basic, in the other hand, uses the not keyword instead of !. Ruby uses both, if I refer to the help files.

 

The "!" signifies logical NOT in B, C, and languages with a C-inspired syntax such as C++, Java, JavaScript, Perl, and PHP. "NOT" is the operator used in ALGOL 60, BASIC, and languages with an ALGOL- or BASIC-inspired syntax such as Pascal, Ada, Eiffel and Seed7. Some languages (C++, Perl, etc.) provide more than one operator for negation. A few languages like PL/I and Ratfor use ¬ for negation. Some modern computers and operating systems will display ¬ as ! on files encoded in ASCII. Most modern languages allow the above statement to be shortened from if (!(r == t)) to if (r != t), which allows sometimes, when the compiler/interpreter is not able to optimize it, faster programs.

(Source: Wikipedia)

 

I guess it's kind of a programming convention? Like something to make logical negation shorter. I don't know for sure where it begins, though, but if I cite the above quote, I think it's a convention made for convenience.

 

As for =/= ... I, too, am curious.

Yeah, I think .each is used by a lot of enumerable classes... which means all you really have to do is define .each and you can use a bunch of other methods for free I think with the 'enumerable' module.
 
Also I imagine =/= was just too long to catch on.
 
As a side note, my favorite little Ruby trick I have learned is ||= actually, I am not sure if any other languages allow you to do that. I mean, say there is a variable that might not be assigned yet, x ||= y will give you the variable unless it's nil (or false) and other wise will set the variable to y and give you y. It's handy!
 
Ruby does some interesting things like that. It seems like it has a kind of 'stack' that it hides from you when calculating things. A major result of this is basically no real difference in Ruby between 'statements' and 'expressions'. Everything returns a value, even if that value is nil. Assignments, if statements, almost everything can be used this way. The final expression/statement in a method is always returned without needing a return keyword for example.
 
One thing Ruby can do as a result that I think would be illegal in most languages is this:
x = if y
  z
else
  n
end
I think in C or something this would need the trinary operator.

Yeah, I think .each is used by a lot of enumerable classes... which means all you really have to do is define .each and you can use a bunch of other methods for free I think with the 'enumerable' module.
 
Also I imagine =/= was just too long to catch on.
 
As a side note, my favorite little Ruby trick I have learned is ||= actually, I am not sure if any other languages allow you to do that. I mean, say there is a variable that might not be assigned yet, x ||= y will give you the variable unless it's nil (or false) and other wise will set the variable to y and give you y. It's handy!
 
Ruby does some interesting things like that. It seems like it has a kind of 'stack' that it hides from you when calculating things. A major result of this is basically no real difference in Ruby between 'statements' and 'expressions'. Everything returns a value, even if that value is nil. Assignments, if statements, almost everything can be used this way. The final expression/statement in a method is always returned without needing a return keyword for example.
 
One thing Ruby can do as a result that I think would be illegal in most languages is this:

x = if y
  z
else
  n
end
I think in C or something this would need the trinary operator.

 

 

I should also try experimenting more with the yield keyword. Say, do you know how the sort {|a,b| ...} method works? It's supposed to sort the contents of a collection of objects using a, b and evaluating them in iterator blocks, but I still can't get how it works.

 

Aaaaand, oh, goodness! Thanks for telling me! I usually do the following to avoid nil:

x = y unless y.nil?

If I haven't erred in understanding, the ||= operator basically assigns the value of y to x unless y is nil, no? Thanks for telling me, this -does- come in handy! :D

 

Also, since everything returns a value, and ruby interprets, in its logical evaluation, that everything is true unless it's nil or false, it -is- handy. At first I thought this was confusing, yet after understanding how it works, it sure comes in handy oftentimes.

 

And... oh is it acceptable to break if modifiers into multilines? I was afraid it would err the code so I usually do it in one line:

x = if y then z else n end
# or even
x = y ? z : e

Thanks for telling me. Breaking the if modifier into multiple lines makes the code easier to read (and I can perform multiple checks and statements inside the if modifier block).

 

The trinary operator...I haven't made use much of them, though, but I'll do learn some of it.

The 'yield' keyword only works with blocks (or fibers kinda). Basically it runs a method's block. Here is an example I found:
 

def calculation(a, b)
  yield(a, b)
end

puts calculation(5, 6) { |a, b| a + b } # addition
puts calculation(5, 6) { |a, b| a - b } # subtraction

 
The '.sort' method mostly works with the special '<=>' operator/method, which basically returns if something is less, equal, or greater. To be exact, I think x <=> y returns -1 if x is less then y, 0 if they are equal, and 1 if x is more then y. Here is another example I found somewhere:

a = [ "d", "a", "e", "c", "b" ]
a.sort                    #Default x <=> y gives ["a", "b", "c", "d", "e"]
a.sort { |x,y| y <=> x }  #Reverse gives ["e", "d", "c", "b", "a"]

b = [ [1,2,3], [4], [5,6] ]
b.sort { |x,y| x.size <=> y.size } #Sort by sise, gives [ [4], [5,6], [1,2,3] ]

Also! And this is important! When you see a method ending in '!' it means it sorts something in place rather then give a new copy. So '.sort' gives you a new copy of an array that is sorted but '.sort!' sorts the array it's self.

a = [ "d", "a", "e", "c", "b" ]
b = a.sort #now b is set to ["a", "b", "c", "d", "e"] but a is unchanged
a.sort!    #now a is set to ["a", "b", "c", "d", "e"]

Also as for if, as for as I am aware if/else uses blocks. I am pretty sure you can't write it any other way, but I wouldn't be surprised if internally it translates to something closer to a method call like lisp's if statements, aka 'if({condition block}, {true block}, {false block})' or something. And as we have seen, we can write multi-line blocks to split a statement like with 'do'. So I am pretty sure 'then' and 'else' in a single line if acts to delimit the block as if it were using '{}' and a return acts kinda like 'do' would? As long as there is an 'end' it just counts as a block and can treat it right so it doesn't have to be on one line. Not sure the technical way of saying it, but yeah I think that's basically what is happening?

 

Side note: For fun, talking about ruby, blocks, if, and lisp reminded me of something delightful I found once. Because ruby coding with all this built in stuff like if statements and numbers is too mainstream. :3

May 2017

S M T W T F S
 123456
78910111213
14151617181920
21222324 25 2627
28293031