Help needed: Dynamically create a Proc from a String and eva


#1

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|
cexp(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:incall’
from (eval):1:in eval_formula' from t00.rb:64:ineval’
from t00.rb:102:in eval_formula' from t00.rb:64:ineach’
from t00.rb:64:in `eval_formula’
from t00.rb:131

Is there a better way to do this ?

Thank you!

Best regards,

Axel


#2

Can we see the code of your eval_formula?

Jason


#3

On Tue, Jun 05, 2007 at 04:18:58AM +0900, Axel E. 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.


#4

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@:1(eval)

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 C. removed_email_address@domain.invalid
An: removed_email_address@domain.invalid
Betreff: Re: Help needed: Dynamically create a Proc from a String and
evaluate it


#5

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


#6

Don’t you mean

g.call(1.0, 2, 5) ?

You have to do proc.call, they aren’t regular methods.

Jason


#7

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