Announcement
Announcement
| 2nd Quarter Contest Announcement posted! See the Community Announcements section. |
![]() ![]() |
Sep 30 2009, 11:30 PM
Post
#1
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
Inspired today by my discovery that Ruby natively supports continuations, I have decided to start a thread which will serve only to showcase the cool 'tricks' that you can do in Ruby. Anyone is allowed (encouraged) to contribute to this thread, but spamming will not be tolerated and posts deemed as spam will be deleted. Also, Mithran, Mr. A, OrigWij, and myself will be the arbiters of what constitutes a 'cool trick'. If your post gets deleted, don't take it personally, it just does not meet our standard of 'cool', i.e., it is common knowledge.
OK, so the rules are simple: 1. Post a complete code sample that can be pasted into irb and run with no modifications. 2. Post an explanation of what the code does and how/why it works. 3. If you are using language features from a new version of Ruby please let us know. 4. You may comment or ask questions about a code sample, but please limit these as much as possible. Questions are great, but if you only wish to comment you should give us a 'cool trick' in turn. Alright, I'll start this off: CODE class Foo def bar catch :break do puts 'starting...' callcc do |@continuation| puts 'pausing...' throw :break end puts 'finished' end end def continue @continuation.call puts 'this line will never be executed' end end CODE # usage f = Foo.new f.bar # starting... # pausing... f.continue # finished This is called a continuation. A concept often used in functional programming languages, this allows us to save the state of a function for non-local execution at a later time. Using this, we can pass around methods as first class objects which hold state of their own. When we start the method 'bar', we set up a 'catch' block and associate a symbol with it: CODE class Foo def bar catch :break do When a 'throw <symbol>' statement is encountered inside of the catch block, the program execution zips up the call stack looking for a matching symbol. When found, the program execution begins again at the end of the catch block. The next interesting bit is this line: CODE callcc do |@continuation| 'callcc' is a method defined in the Kernel module (Kernel#calcc). What it does is a bit abstract, so don't feel bad if you do not get it the first time you read this. Here is the official definition: QUOTE callcc {|cont| block } => obj Generates a Continuation object, which it passes to the associated block. Performing a cont.call will cause the callcc to return (as will falling through the end of the block). The value returned by the callcc is the value of the block, or the value passed to cont.call. See class Continuation for more details. Also see Kernel::throw for an alternative mechanism for unwinding a call stack. So, what does this mean? We call the method which generates for us a Continuation object (@continuation) and then pass in a block of code. This continuation object is passed to our block, but we don't do anything with it yet. When we call 'throw :break', we unwind the call stack, preventing the callcc method from returning, and its state is saved in the continuation object for use at a later time. The last statement in our method: CODE puts 'finished' Will not be called due to out 'throw' statement. So, we want to finish our method. We do so by calling our second method, "continue": CODE def continue @continuation.call puts 'this line will never be executed' end All that method does is use the saved continuation object (@continuation) to re-enter our method where we left off. Because the callcc will return at the end of the code block, the 'puts' statement will never be executed. This is a rather contrived example, but continuations can be used for all sorts of neat (read: hard to understand) things in programming. Hope to see more posts soon! -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Oct 1 2009, 02:27 AM
Post
#2
|
|
![]() I script my own reality ... ![]() Type: Coder Alignment: Chaotic Evil |
I have 2 little snippets that have cool functions ....
1) When using aliases, add "unless $@" to the end of it like so: CODE alias new_method_name old_method_name unless $@ def old_method_name p "same method" end This will prevent those pesky "stack overflow" errors when F12 is pressed. What it does is not allow the method to be re-aliased when the system is soft-reset by F12. Note by BigEd781: I would just add that what we are really checking here is whether $@, an array of exceptions, is nil or not. If it is not nil, an exception was thrown up the stack and we o not re-alias the method. 2) The infamous === ... thats right ... three equals in a row. It's used to compare a variable to a range. For example: CODE if (13..42) === item.id If the item's ID is in the range of 13 to 42, it will print said ID.p item.id end The range can also use "..." to not include the maximum iterator: CODE if (10...data.size) === index If the index is in the range of 10 to data.size - 1, it will print that part of the "data" array.p data[index] end !== does the same thing but checks to see if the variable is NOT in the range CODE if (x..y) !== counter If the counter is NOT in the range of x to y, it will print the counterp counter end NOTE: The range must ALWAYS be to the left of the === : CODE if (12..69) === index <--- correct
if index === (12..69) <--- incorrect -------------------- |
|
|
|
Oct 1 2009, 04:54 AM
Post
#3
|
|
![]() Scripter ![]() Type: Coder Alignment: True Neutral |
Deep Cloning an object
Objects can be cloned by calling the object#dup or object#clone method on them, as such. These methods, however, produce only a shallow clone of an object, meaning that while the object itself is considered a new object, all of the object's properties or instance variables are still considered the same objects: Spoiler: We could write complex cloning methods that make sure all the objects held within are themselves clone, but there is an easier way. This is to use the Marshal module to serailize and immediately load the data into a new object. This only works if the object allows serialization, meaning it has a defined __dump and __load (true for most objects, but dont try this with the Window or Proc class). CODE obj2 = Marshal.load(Marshal.dump(obj1)) We are serializing obj1 into a string and immediately reconstituting the result into obj2. All objects held by instance variables within obj1 are considered new objects when loaded into obj2. This means when we alter the array in obj2, it is a now different array than the one held by obj1.an_array: Spoiler: The examples may be a bit contrived, but it is possible to have arrays nested in hashes further nested in arrays and you want to have a working copy of all the data. Another useful application is to deep clone an actor object. Actors carry around all sorts of objects that you would want to use new objects for during a clone. For example (these assume you are working within the script editor, with a minimum of the default scripts): Spoiler: -------------------- My scripts:
Spoiler: Searching for one of my snippets? Mithran's Snippet Repository My current scripting projects: Mithran's Script Development Thread Who says I'm always idle in the IRC? Spoiler: |
|
|
|
Oct 1 2009, 09:36 PM
Post
#4
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
CODE x = %w(this is a test) x *= 2 =>["this", "is", "a", "test", "this", "is", "a", "test"] When you use the '*' operator on an array (Array#*) with a numerical value, it multiplies the size (and contents) of the array, i.e., the array is duplicated n times. However, look at this code: CODE x = %w(this is a test) x *= ',' =>"this,is,a,test" Instead of using an integral value, we have passed a string to the Array#* method. When the input is a string, a join is automatically performed on the array! -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Oct 1 2009, 09:47 PM
Post
#5
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
This one is very handy for formatting output, the String#% method:
CODE hp = 123 formatted_hp = "%.4d" % hp =>"0123" money = 10.5 formatted_money = "$%.2f" % money =>"$10.50" scores = [1,2,3] formatted_scores = "Billy: %d, Susan: %d, Janet: %d" % scores =>"Billy: 1, Susan: 2, Janet: 3" The first string is a format specifier, and the second argument is an array of values to replace the format specifiers in the first string. This is really just a shortcut for Kernel#sprintf (all format specifiers can be found by following that link). -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Oct 1 2009, 09:56 PM
Post
#6
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
And another neato Ruby feature, the splat operator
This operator is used to "explode" enumerables. look at this code: CODE a, b, c = *[1, 2, 3] a => 1 b => 2 a => 3 This can also be used in reverse: CODE *x = 1, 2, 3 x => [1,2,3] So, when you see this construct: CODE class Foo alias :old_bar :bar def bar(*args) old_bar(*args) # do new stuff here end end Now you know what is going on. We just 'slurp' the arguments passed to Foo#bar into an enumerable collection, and then 'explode' them again when we call the aliased method. This allows us to alias the bar method without specifying an argument list (since we are not using them anyhow). Now, if someone comes along and changes the method signature of Foo#bar behind our backs our code still works. -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Oct 1 2009, 11:54 PM
Post
#7
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
Almost forgot one of my favorites. Instead of writing code like this:
CODE def some_method @foo = 0 if @foo.nil? end We can just write this: CODE def some_method @foo ||= 0 end If @foo is nil (or false), we assign 0 to it, otherwise leave it as is. This is a little sneaky. Most people will assume that this is going on behind the covers (myself included in the beginning): CODE @foo = @foo || 0 But that is not the case. The true expansion is: CODE @foo || @foo = 0 That works because, if @foo evaluates to true (i.e., has a value other than nil or false) the right side will not be evaluated and @foo will be returns. However, if @foo evaluates to false (remember; nil evaluates to false) then the right hand side is evaluated, 0 is assigned to @foo, and the result of the expression (0) is returned. Also this can help to cut down on lines when declaring local variables (just saw this usage for the first time earlier today): CODE (x ||= []) << 'test' So, instead of defining a local variable with some initial content (often just an empty hash or array), you can instead define it "on the go" so you can perform operations on it at the same time. This would replace this construct: CODE x = []
x << 'test' -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Oct 4 2009, 12:49 PM
Post
#8
|
|
![]() Scripter ![]() Type: Coder Alignment: True Neutral |
While not widely used among RGSS2 scripts, it is entirely possible to define methods within other methods. Here, we have a class method define an instance method:
Spoiler: A method can even alias and redefine itself: Spoiler: This example defines do_something to print "before", then aliases itself as do_something_before and redefines itself to print "after", followed by redefining itself to do what it did in the first place (execpt to skip the alias, because the method is now defined when this line is reached and the line is qualified with an unless defined?). Or with a Singleton: Spoiler: Since the mod_method is an instance method, the value of 'self' at this point is the current instance object. Therefore, the encased method definition is for the current instance, or a Singleton method for the object. -------------------- My scripts:
Spoiler: Searching for one of my snippets? Mithran's Snippet Repository My current scripting projects: Mithran's Script Development Thread Who says I'm always idle in the IRC? Spoiler: |
|
|
|
Dec 3 2009, 01:52 AM
Post
#9
|
|
![]() ![]() Type: Designer Alignment: Neutral Good |
I found multidimensional arrays to be cool and quite useful. I'm certain our programming verterans are familiar with them, but for everybody else, a multidimensional array is very much like a normal array, except instead of storing values as a list, you can store values as a grid.
In order to create a multidimensional array, you need a method to construct it. Here's the method: CODE def mda(row,column) Array.new(row).map!{ Array.new(column) } end When you declare your multidimensional array, you need to specify it's height and width. To create a multidimensional array with a size of 10x10, declare it like this: CODE example_mda = mda(10,10) Now to store and retrieve values: CODE example_mda[2][5] = "test string" example_mda[3][3] = 7 print example_mda[2][5] # This will print "test string" print example_mda[3][3] # This will print "7" This post has been edited by Prof. Meow Meow: Dec 3 2009, 01:53 AM -------------------- 8 lives remaining.
|
|
|
|
Jan 20 2012, 04:17 AM
Post
#10
|
|
![]() I'm on fire 24/7 >:3 ![]() Type: Coder Alignment: Lawful Good |
Hope this necropost is worthy enough..that said, I've been messing around with Hash#method_missing, and have come up with this:
CODE class Hash alias method_missing_no_key method_missing def method_missing(method,*args,&block) sym = method.to_s.gsub("=","").to_sym if method.to_s[-1].chr == "=" self[sym] = args[0] end return self[sym] end end Basically, when you call a method of an object and it is not found, #method_missing is called. The user can then modify this method to do what they wish, as long as they allow the default methods contents to work through as well. But, I wanted to call a hash's keys like methods, so I decided I'd utilize this function; simply catch the method being called, determine whether it is a setter or getter method, and operate on top of that. So, this will not error when used in conjunction with the above code: CODE hash = {}
puts hash #=> {} hash.owo = "owo" puts hash.owo #=> "owo" puts hash.owo == hash[:owo] #=> true puts hash #=> {:owo => "owo"} |
|
|
|
![]() ![]() |
| Lo-Fi Version | Time is now: 20th May 2013 - 01:14 AM |
|
|