Dear all, I need help in dynamically creating a Proc from a String like, say, a='f(x)=c*exp(x/k)', (I need to do this many times for many different formulas) which I want to use Ruby to calculate as a function of x, given constants c,k, i.e., In this example, I want the following lines to produce, b=a.generate_formula => "def f(x)\n\n return Proc.new{|c,x,k| c*exp(x/k) }\n end \n" c=a.eval_formula({'c',5.0,'k',2.0,'x',1.0}) => 5*exp(0.5) The first line is o.k., and I manage to change the string b to d="f(1.0).call(5.0,2.0)", but I can't eval it using eval(b) # (should make the function available as a Proc method) eval(d) # (should output the value at x=1.0) because the first variable,x , doesn't get its value assigned: t00.rb:102:in `eval_formula': (eval):6:in `/': nil can't be coerced into Float (TypeError) from (eval):6:in `Ie' from (eval):1:in `call' from (eval):1:in `eval_formula' from t00.rb:64:in `eval' from t00.rb:102:in `eval_formula' from t00.rb:64:in `each' from t00.rb:64:in `eval_formula' from t00.rb:131 Is there a better way to do this ? Thank you! Best regards, Axel

on 2007-06-04 21:20

on 2007-06-04 21:47

Can we see the code of your eval_formula? Jason

on 2007-06-04 21:56

On Tue, Jun 05, 2007 at 04:18:58AM +0900, Axel Etzold wrote: > I need help in dynamically creating a Proc from a String like, say, > > a='f(x)=c*exp(x/k)', If you're passing in c and k each time, then b = eval("proc{|c,x,k| c*Math.exp(x/k) }") puts b.call(5.0, 2.0, 1.0) This means you only call eval once, to create the proc object, then you can call it as many times as you like. If c and k really are constants then you can substitute them in to the proc definition: c = 5 k = 2 a = eval("proc { |x| #{c}*Math.exp(x/#{k}) }") puts a.call(3) or you can use the closure property to bind the local variables directly: c = 5 k = 2 a = eval("proc { |x| c*Math.exp(x/k) }") puts a.call(3) Brian.

on 2007-06-04 22:06

Dear Jason, thank you for responding. Here is what I have so far ( I hope the formatting won't be destroyed): class String def generate_formula # left-hand side of equation gives the function f=self.split(/\=/) formula='def ' + f[0] args=f[1].gsub(/[^\+\-\*\/]+\(/,'').gsub(/\)/,'').split(/[\/\*\-\+]/).uniq args_new=args.collect{|x| if x==x.capitalize ; x='_' + x; else x=x; end} rhs=f[1] lhs=f[0] args.each_with_index{|x,i| if args_new[i]!=x lhs.gsub!(x,args_new[i]) rhs.gsub!(x,args_new[i]) end } formula='def ' + lhs + "\n" #formula<<'p ' + lhs #formula<<'p ' + rhs formula<<"\n return Proc.new{|" + args_new.join(',') + '| ' formula<< rhs + ' }' formula<<"\n end \n" name=f[0][0...f[0].index('(')] # return the following: # 1) formula=String containing Proc method to be evaluated # 2) name of the Proc method # 3) Array of arguments ( variables and constants occurring # in the original formula # 4) arguments from 3) changed to non-uppercase, if needed return formula,name,args,args_new end def eval_formula(hash) f,name,vars,new_vars=self.generate_formula # do this to change uppercase variables, if needed hash2={} hash.each{|k,v| ind=vars.index(k) if v.class!=Array v=[v] end hash2.store(new_vars[ind],v) } # generate string for Proc to be evaluated ind1=f.split(/\n/)[0].index('(') ind2=f.split(/\n/)[0].index(')') args_left=f.split(/\n/)[0][ind1+1...ind2] other_args=new_vars-args_left.split(',') eval_fn = name + '(args_left)' max_nr_of_vals=[] # how many evaluations ? # to do: check that number of changed values # is either 1 or coincides for all variables hash2.values.each{|x| max_nr_of_vals<<x.length } # to evaluate Proc (later), substitute constant values ev_hash={} for k in 0...max_nr_of_vals.max ev_hash_text=eval_fn + '.call(' for my_var in other_args if hash2[my_var].length>1 this_val=hash2[my_var][k] # several values of "constants" else this_val=hash2[my_var][0] # just one value end ev_hash_text<<this_val.to_s + ',' end ev_hash_text.chop! ev_hash_text<<')' repl='' # substitute actual function values args_left.each{|y| for m in hash2[y] repl<<m.to_s + ',' end } ev_hash_text.sub!('args_left',repl.chop) # this is the call to the formula with substituted values p ev_hash_text eval(ev_hash_text) end end end a='f(x)=c*exp(x/k)' b=a.generate_formula a.eval_formula({'c',[5.0],'k',[2.0],'x',[1.0]}) Thank you, Axel

on 2007-06-04 22:45

Dear Brian, thank you for responding. However, I still haven't solved my problem. So, following one of your suggestions, I get: p 'function-text' p f => "eval('proc{|c,x,k| c*Math.exp(x/k) }')" g=eval(f)=> #<Proc:0x00002b56c768ef68@(eval):1> p g(1.0).call(2,5) => undefined function or variable 'g' I can't get to use the proc I just created.. Why not ? Thank you, Axel -------- Original-Nachricht -------- Datum: Tue, 5 Jun 2007 04:55:57 +0900 Von: Brian Candler <B.Candler@pobox.com> An: ruby-talk@ruby-lang.org Betreff: Re: Help needed: Dynamically create a Proc from a String and evaluate it

on 2007-06-04 22:53

Don't you mean g.call(1.0, 2, 5) ? You have to do proc.call, they aren't regular methods. Jason

on 2007-06-04 23:05

Dear Jason, This: g.call(1.0, 2, 5) worked. I made a mistake copying text from the Ruby Proc webpage. Thank you both very much! Best regards, Axel