3

I am trying to generate a lambda programmatically. This is an example:

This is the matrix that I need to generate:

m = [[a(t), b(t)],
     [c(t), d(t)]]

This is a time dependent matrix. If the a, b, c, d functions were always the same I would create a lambda like this:

m = lambda{|t| [[a(t), b(t)], [c(t), d(t)]]}

and call it with:

m.call(x)

The functions are not completely general, but they can come only from a limited list. The problem is that I don't know which ones, from this list, the functions are going to be before I perform some calculations. In my case, for example, I have only three possible functions, so the lambda could be:

m = lambda{|t| [[f1(t), f2(t)], [f2(t), f3(t)]]}

or

m = lambda{|t| [[f3(t), f3(t)], [f1(t), f2(t)]]}

or any other combination of the three functions.

Is there a way to define lambdas programatically? Is this the best approach?

In the real code this matrix could be quite large, easily 500x10,000 (500,000) elements. It is calculated in a first loop. After that the loop goes through t values. For each t the functions do not change of course. Also, these functions are simple mathematical expressions such power, exp, etc...

Rojj
  • 1,170
  • 1
  • 12
  • 32

1 Answers1

5

You could use lambdas for your fs as additional arguments to m:

m = lambda { |t, a, b, c, d| [[a[t], b[t]], [c[t], d[t]]] }

f1 = lambda { |x| x**2 }
f2 = lambda { |x| x**3 }
f3 = lambda { |x| x**4 }

p m[2, f1, f2, f2, f3]  # [[4, 8], [8, 16]]
p m[2, f3, f3, f1, f2]  # [[16, 16], [4, 8]]

Based on your expanded question, here's an approach which is more dynamic and can be scaled to large matrices. I'm assuming that you have some algorithmic way to determine which lambdas are needed at which point in your algorithm, but since I have no idea what that is I've used a tabled specification.

# Dynamically create a 2-d array of lambdas, determined by an
# index set referencing the pool of lambdas, with specified row_length
m = lambda do |f_set, index, row_length|
  Array.new(index.size) { |i| f_set[index[i]] }.each_slice(row_length).to_a
end

# Function to evaluate actual outcomes yielded by applying
# the lambdas in m to argument x.
def evaluate(m, x)
  m.map { |row| row.map { |lmb| lmb[x] } }
end

# Pool of lambdas used to generate m
f_set = [
  lambda { |x| x**2 },
  lambda { |x| x**3 },
  lambda { |x| x**4 }
]

# You'll need some way to specify which lambdas will be used,
# either with table lookup (as below), or preferably via some
# algorithm if the array sizes will be large.
indices = [[0, 1, 1, 2, 0, 1], [2, 2, 0, 1, 0, 1]]

# create different sets of arrays m populated with lambdas as
# specified by indices to the f_set.
m_3x2 = indices.map { |index_set| m[f_set, index_set, 2]}
m_2x3 = indices.map { |index_set| m[f_set, index_set, 3]}

m_3x2.each { |m| p evaluate(m, 2) }
# [[4, 8], [8, 16], [4, 8]]
# [[16, 16], [4, 8], [4, 8]]
m_2x3.each { |m| p evaluate(m, 2) }
# [[4, 8, 8], [16, 4, 8]]
# [[16, 16, 4], [8, 4, 8]]


m_3x2.each { |m| p evaluate(m, 3) }
# [[9, 27], [27, 81], [9, 27]]
# [[81, 81], [9, 27], [9, 27]]
m_2x3.each { |m| p evaluate(m, 3) }
# [[9, 27, 27], [81, 9, 27]]
# [[81, 81, 9], [27, 9, 27]]
pjs
  • 18,696
  • 4
  • 27
  • 56
  • Rojj, you asked, "Is there a way to define lambdas programatically [dynamically]?". Clearly, pjs's lambda, `m`, is not created dynamically. If you find `a[t]` puzzling, it's the same as `a.call(t)` (and also the same as `a.yield(t)`, `a===2` and [deprecated] `a.(2)`). See [Proc](http://ruby-doc.org/core-2.4.0/Proc.html). Lastly, you could use a method instead of a lambda if you don't need to pass it around. – Cary Swoveland Feb 26 '18 at 07:15