Let's take a look at a question. This is a question on codewars. I have seen it very early, but I won't write it. When I saw this question again, I had just read the Yuan programming book and thought it was time to solve it. Not much nonsense. Let's take a look at this question first. The question was first written for Javascript, but it was also included in the ruby language. It doesn't matter. The question content is a class of calc, which can be called through chained methods to implement addition, subtraction, multiplication, division. Here are four examples. The number is only 0-9, the operation only has addition, subtraction, multiplication, division, and each operation only has one operator. (You can skip the following and think about how to write it first)
First, each example has the same structure-class name and four methods. Taking the first example as an example, Ruby calls are generally in the form of objects and methods. The initial idea is to construct a new method for the calc class (of course, the initialize method is automatically called when new is called. If only an instance object is returned, this method is not required. create a one method for the new object, which is Calc. new. one object builds a plus method for Calc. new. one. build a two method for the plus object.
In this way, the singleton method seems to have no problem. Although it may be complicated, it should be done. So how to write it? (I don't know. If you have any ideas, please advise)
My idea is similar to the following. Please refer to the code
1 class A
2 def one
3 def self.plus
4 def self.one
5 p "one+plus+one"
6 end
7 self
8 end
9 self
10 end
11 end
The problem now is to replace the method name with a variable, but how can we pass the parameter in this way? This is a problem. So this idea is stranded.
For more information about how to define method nesting, see http://blog.csdn.net/kiwi_coder/article/details/8122085.
One method is another method. In the previous method, I forgot to return the value self. Therefore, Nil is often unavailable. So I thought of method_missing. Method_missing in ruby is used to find a solution in method_missing when this object does not have a method. The method_missing method uses the method name as a parameter, so it can be called directly.
The Code is as follows:
1 class Calc
2 # Implement here
3 @@str=""
4 @@time = 0
5 def method_missing(name)
6 has = { :one => 1,:two=>2,:three => 3,:four=>4,:five => 5,
7 :six=>6,:seven => 7,:eight=>8,:nine => 9,:zero=>0,}
8
9 mth ={:plus=>"+",:minus=>"-",:times=>"*",:divided_by=>"/"}
10 @@str = "" if @@time == 3
11 @@str << has[name].to_s if has.has_key?(name)
12 @@str << mth[name] if mth.has_key?(name)
13 @@time += 1
14 @@time -= 3 if @@time > 3
15 if @@time == 3
16 eval @@str
17 else
18 self
19 end
20 end
21 end
When the method name is passed in method_missing, two hash values are defined first, the value corresponding to the method name is written to a variable string @ STR, and the eval is used to execute the string. @ Time is used to calculate the number of methods. Each operation is performed, and the string is cleared. It is worth noting that if no execution is performed, self is returned.
This code is the code I submitted. It only completes the function, but it is not well written.
Disadvantages:
1. Eval is used. This method is not recommended in various books.
2. In fact, two hashes can be merged, which is not a problem.
3. My method only combines the method name into a string, which is a bit opportunistic and cannot be used for more operations.
After the submission, I read other people's code. Share with you the most recommended ones.
The Code is as follows:
1 # Chainable:
2 # Calc.new.one.plus.one.plus.one == 3
3
4 class Fixnum
5 def plus; Calc.new("+", self) end
6 def minus; Calc.new("-", self) end
7 def times; Calc.new("*", self) end
8 def divided_by; Calc.new("/", self) end
9 end
10
11 class Calc
12 def initialize(*arguments)
13 if arguments.length == 2
14 @operation = arguments[0]
15 @number = arguments[1]
16 end
17 end
18
19 %w(zero one two three four five six seven eight nine).each_with_index do |w,i|
20 define_method(w) { perform i }
21 end
22
23 def perform number
24 if @operation
25 @number.send(@operation, number)
26 else
27 number
28 end
29 end
30 end
For a brief analysis, lines 4-9 define addition, subtraction, multiplication, division in the fixnum class, and return the calc object with two parameters. 12-17 rows are the initialization process. Line 19-21 defines the corresponding 0-9 method, and each method content is the execution of the perform method. Line 23-29 defines the perform method. Use Calc. New. One. Plus. Two to explain that Calc. New does not contain parameters, so an instance object of calc is returned. Calc. New. One, call the instance method one,
Execute perform 1. At this time, @ operation has no value. Therefore, the value of Number 1 is returned. Calc. New. One. Plus, 1 is an instance of the fixnum class. Calling the plus method returns a calc. New object with two parameters. Therefore, the @ operation and @ number values are respectively '+' and 1 (here, self is 1 ). The Calc. New object calls the two method again. @ operation is available at this time. Therefore, the content in the IF Condition Statement is executed. Expected result 3 is displayed.
This method can also execute a longer method, such as calc. New. One. Plus. Two. Minus. Three.
To sum up, chained access should clarify the object of each method. The return value of each method is the object of the next method.