HOME 備忘録 書き込む

関数電卓 Ruby 版


May 05, 2008

Scheme 版, Python 版, JavaScript 版 に続いて今回は Ruby 版です。 基本的に Python 版と同じです。 起動時の引数がない場合は対話モードで、 ファイル名を引数に与えると、そのファイルを開いて 1行ずつ計算していきます。

紫藤は Python に慣れているので Python の方が書きやすかったです。 Ruby は

と感じました。

興味のある人はダウンロードして遊んでみてください。 また、いけてないところが多々あると思いますので、ご指摘いただけたら幸いです。

[calc.rb]

001:   #! ruby
002:   # calculator, ruby version 
003:   
004:   
005:   
006:   P_CST=6
007:   P_LHB=5
008:   P_FUN=4
009:   P_POW=3
010:   P_UPM=2
011:   P_MD=1
012:   P_BPM=0
013:   
014:   
015:   
016:   
017:   class Operator
018:       
019:     def initialize(name, fun, priority)
020:       @name=name
021:       @fun=fun
022:       @priority=priority
023:     end
024:       
025:     def to_s
026:       return @name 
027:     end
028:       
029:     def call(x=nil, y=nil)
030:       if self.is_constant then return @fun
031:       elsif self.is_unary or self.is_lhb then return @fun.call(x)
032:       elsif self.is_binary then return @fun.call(x,y)
033:       end
034:     end
035:       
036:     def >(other)
037:       return (other==nil or @priority > other.priority or (self.is_reverse and @priority==other.priority))
038:     end
039:   
040:     def priority
041:       return @priority
042:     end
043:       
044:     def is_constant
045:       return @priority==P_CST
046:     end
047:   
048:     def is_lhb
049:       return @priority==P_LHB
050:     end
051:   
052:     def is_func
053:       return @priority==P_FUN
054:     end
055:   
056:     def is_unary
057:       return (@priority == P_FUN or @priority == P_UPM)
058:     end
059:   
060:     def is_upm
061:       return @priority == P_UPM
062:     end
063:       
064:     def is_binary
065:       return (@priority == P_MD or @priority == P_BPM or @priority == P_POW)
066:     end
067:     
068:     def is_reverse
069:       return (@priority == P_FUN or @priority == P_UPM or @priority == P_POW)
070:     end
071:   end
072:   
073:   
074:   def fact(n)
075:     if not n.is_a?(Integer) then 
076:       raise "fact needs integer"
077:     end
078:     if n < 0 then
079:       raise "fact needs positive number"
080:     end  
081:     if n==0 then return 1
082:     else
083:       j=1
084:       for i in 1 .. n
085:         j*=i
086:       end
087:       return j
088:     end
089:   end
090:   
091:   def permutation(m,n)
092:     if not ( n.is_a?(Integer) and m.is_a?(Integer) ) then 
093:       raise "P needs integer"
094:     end
095:       
096:     if n>m then
097:       raise "LHS should be larger or equal to RHS"
098:     end    
099:   
100:     return fact(m)/fact(m-n)
101:   end
102:   
103:   
104:   def combination(m,n)
105:     if not ( n.is_a?(Integer) and m.is_a?(Integer) ) then 
106:       raise "C needs integer"
107:     end
108:   
109:     return permutation(m,n)/fact(n)
110:   end
111:   
112:   
113:   L_OP=[
114:         Operator.new('@+', lambda{|x| +x}, P_UPM),
115:         Operator.new('@-', lambda{|x| -x}, P_UPM),
116:         Operator.new('+', lambda{|x,y| x+y}, P_BPM),
117:         Operator.new('-', lambda{|x,y| x-y}, P_BPM),
118:         Operator.new('*', lambda{|x,y| x*y}, P_MD),
119:         Operator.new('/', lambda{|x,y| x/y}, P_MD), 
120:         Operator.new('%', lambda{|x,y| x%y}, P_MD),
121:         Operator.new('**', lambda{|x,y| x**y}, P_POW),
122:         Operator.new('^', lambda{|x,y| x**y}, P_POW),
123:         Operator.new('exp', lambda{|x| Math.exp(x)}, P_FUN),
124:         Operator.new('log', lambda{|x| Math.log(x)}, P_FUN),
125:         Operator.new('log10', lambda{|x| Math.log10(x)}, P_FUN),
126:         Operator.new('sqrt', lambda{|x| Math.sqrt(x)}, P_FUN),
127:         Operator.new('sin', lambda{|x| Math.sin(x)}, P_FUN),        
128:         Operator.new('cos', lambda{|x| Math.cos(x)}, P_FUN),
129:         Operator.new('tan', lambda{|x| Math.tan(x)}, P_FUN),                
130:         Operator.new('asin', lambda{|x| Math.asin(x)}, P_FUN),                
131:         Operator.new('acos', lambda{|x| Math.acos(x)}, P_FUN),
132:         Operator.new('atan', lambda{|x| Math.atan(x)}, P_FUN),
133:         Operator.new('pi', Math::PI, P_CST),
134:         Operator.new('e', Math::E, P_CST),
135:         Operator.new('!', lambda{|x| fact(x)}, P_LHB),
136:         Operator.new('P', lambda{|x,y| permutation(x,y)}, P_POW),
137:         Operator.new('C', lambda{|x,y| combination(x,y)}, P_POW),
138:       ]
139:   
140:   H_OP=Hash.new 
141:   L_OP.each{|op| H_OP[op.to_s] = op}
142:   
143:   
144:   $r_fe=Regexp.new(
145:           '^(?:(\()|(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(' +
146:           L_OP.delete_if{|op|
147:                      op.is_upm
148:                    }.map{|op| 
149:                      op.to_s
150:                    }.sort{|x,y|
151:                      y.length <=> x.length
152:                    }.map{|s|
153:                      s.split('').map{|c| c.match(/w/) ? c : ('\' + c)}.join('') +
154:                      (H_OP[s].is_constant ? '(?=\W|$)' : H_OP[s].is_func ? '(?=\s|\()' : '')
155:                    }.join('|') +
156:                   '))')
157:   
158:    
159:   
160:   def find_close(s)
161:     pos=0
162:     count=0
163:     s.each_byte{|c|
164:       count += (c==40 ? 1 : c==41 ? -1 : 0) # ( -> 40, ) -> 41
165:       if (pos > 0 and count==0) then
166:         return pos 
167:       end
168:       pos+=1
169:     }
170:     return -1
171:   end
172:   
173:   
174:   
175:   def read_input(s0, ls=[])
176:   
177:     s0.strip!
178:   
179:     if s0.length==0 then
180:       return ls
181:     end
182:   
183:     m=$r_fe.match(s0)
184:     if not m then 
185:       raise "cannot parse input"
186:     end
187:   
188:     if m[1] then
189:       j=find_close(s0)
190:       read_input(s0[j+1, s0.length], ls << read_input(s0[1, j-1]))
191:     elsif m[2] then
192:       read_input(s0[m.end(2), s0.length], ls << (m[2].match(/^d+$/) ? m[2].to_i : m[2].to_f))
193:     elsif m[3] then
194:       read_input(s0[m.end(3), s0.length], 
195:                  ls << H_OP[ 
196:                             ((m[3]=='+' or m[3]=='-') and 
197:                             (ls.length==0 or ls[-1].class== Operator)) ?
198:                             '@' + m[3] : m[3]
199:                            ])
200:     end
201:   end
202:   
203:   
204:   def operator_position(ls)
205:     max_operator=nil
206:     j=-1
207:     ls.each_index{|i|
208:       obj=ls[i]
209:       if (obj.class==Operator and obj>max_operator) then
210:         max_operator=obj
211:         j=i
212:       end
213:     }
214:     return j 
215:   end
216:   
217:   
218:   def fappend(ls, val, pos1, pos2)
219:     return (ls[0, pos1] << val) + ls[pos2, ls.length]
220:   end
221:   
222:   
223:   def is_number(ls)
224:     return (ls.class==Float or ls.is_a?(Integer))
225:   end
226:   
227:   
228:   def eval_formula(ls)
229:     if is_number(ls) then 
230:       return ls
231:     elsif ls.class==Operator and ls.is_constant then
232:       return ls.call
233:     elsif ls.length==1 then 
234:       return eval_formula(ls[0])
235:     else
236:       pos = operator_position(ls)
237:       op = ls[pos]
238:       if op.is_constant then
239:         return eval_formula(fappend(ls, op.call, pos, pos+1))
240:       elsif op.is_unary and pos < ls.length - 1 then
241:         return eval_formula(fappend(ls,  op.call(eval_formula(ls[pos+1])), pos, pos+2)) 
242:       elsif op.is_lhb and pos > 0 then
243:         return eval_formula(fappend(ls, op.call(eval_formula(ls[pos-1])), pos-1, pos+1)) 
244:       elsif op.is_binary and 0 < pos and pos < ls.length - 1 then
245:         return eval_formula(fappend(ls, op.call(eval_formula(ls[pos-1]), eval_formula(ls[pos+1])), pos-1, pos+2)) 
246:       else
247:         raise "invalid formula"
248:       end
249:     end
250:   end
251:   
252:   
253:   #------
254:   
255:   interactive= (ARGV.length==0)
256:   
257:   if interactive then
258:     print "avairable opereters and constants are:
"
259:     print L_OP.delete_if{|op| op.is_upm}.map{|op| op.to_s}.join(', ')
260:     print "
"
261:   end
262:   
263:   while 1
264:     if interactive then print '> ' end
265:     s=gets
266:     if not s then break end
267:     s.chomp!
268:     if s.length>0 then
269:       if (interactive and s=='q') then break end
270:       begin
271:         p eval_formula(read_input(s))
272:       rescue => em
273:         p em
274:       end
275:     else
276:       print "
"
277:     end
278:   end