2
$\begingroup$

I'll put the question right here, but will add some context below in case someone wonders why the heck I'm trying to do this.

Why is it that I have to do two passes to get the value of the arguments in the call to f1 below?

In[24]:= s = { 10 Cos[-Pi t], -4 Sin[0.23 t]};

In[25]:= f2[a1_, a2_, a3_] := Module[
           {a = a1, phi = a3},
           Function[
              a a2[phi #1]
           ] (* #1 is the time *)
          ]

         f1 := Function[
                 f2[#1, #2, #3]
         ]

In[27]:= l = Cases[s, amp_ trig_ [afreq_ t] -> f1[amp, trig, afreq]]
         %[[1]][.33]

Out[27]= {a$1667 Cos[phi$1667 #1] &, a$1667 Sin[phi$1667 #1] &}
         (* I expected amp and afreq to have been replaced by
            their matched values, and a numerical value here *)
Out[28]= amp Cos[0.33 afreq]

         (* It works as I (naïvely) expected when I introduce this temporary symbol, func *)
In[29]:= l = Cases[s, amp_ trig_ [afreq_ t] -> func[amp, trig, afreq]]
         % /. func -> f1
         %[[1]][.33]

Out[29]= {func[10, Cos, \[Pi]], func[-4, Sin, 0.23]}

Out[30]= {a$1668 Cos[phi$1668 #1] &, a$1669 Sin[phi$1669 #1] &}

Out[31]= 5.09041

Context: I'm a total noob to Mathematica, but I do ok with plenty of other progamming languages. This issue came up in a structural dynamics course I'm teaching, using Mathematica as a support tool. Not that it should matter here, but I'm using it to compute the dynamic response of a SDOF system to a periodic excitation.

I compute the Fourier series expansion of the forcing function, then apply a function that I've written to all terms of the expansion and then sum it all up. I have removed all of the details of this to concentrate on the essence of the problem, but I'd be more than happy to post the entire functions in case it helps.

I come from a hpc background, where we tend to overcomplicate things in search of "efficiency", bear that in mind :) The general ideal is that the response is a function of many physical parameters, and the time, but for a single term of the expansion, only the time varies and the physical parameters are fixed.

I wrote a function that takes all the physical constants and forcing terms, computes some constants as local variables inside a module, and returns a function of time using these constants.

For all terms of the expansion, however, the physical parameters are the same, just the forcing terms are different, so I wrote another function that takes the physical constants, and returns a function that takes only the forcing terms, combines them with the physical parameters and calls the function above, returning a function that depends only on time, and has all that is constant precomputed.

It's worth noticing that the variable that is not local to the module always gets substituted.

Even though the original question has been answered, I'm adding the original functions here as a justification for my roundaboutery...

fresp[F0_, w_, m_, c_, k_, trig_] :=
  Module[{phi = ArcTan[k - m w^2, c w], A = F0/Sqrt[(k - m w^2)^2 + (c w)^2]},
  Function[ t, A trig[w t - phi]]]
]

tresp[ m_, c_, k_] := Function[ 
  fresp[#1 (*F0*), #2(*w*), m, c, k, #3(*trig*)]
]

mck1 = tresp[2., 2., 3.]
mck2 = tresp[1., 1., 2.]

The idea being that each mck function would represent the response of a different physical system. I apologize if my mind has been poisoned by object orientation :)

$\endgroup$

2 Answers 2

2
$\begingroup$

Look at Rule (->) and RuleDelayed (:>) in the documentation.

s = {10 Cos[-Pi t], -4 Sin[0.23 t]};
f2[a1_, a2_, a3_] := 
  Module[
   {a = a1, phi = a3},
   Function[a a2[phi #1]] (*#1 is the time*)];
   f1 := Function[f2[#1, #2, #3]];

Compare:

l = Cases[s, amp_ trig_[afreq_ t] -> f1[amp, trig, afreq]];
%[[1]][.33]

amp Cos[0.33 afreq]

l = Cases[s, amp_ trig_[afreq_ t] :> f1[amp, trig, afreq]];
%[[1]][.33]

5.09041

$\endgroup$
2
  • $\begingroup$ Thank you! It works beuatifully. And I think I even understand why! $\endgroup$
    – Ramiro
    Jun 6, 2014 at 20:17
  • $\begingroup$ @Ramiro You're welcome, and welcome to the forum :) $\endgroup$
    – mfvonh
    Jun 6, 2014 at 21:27
1
$\begingroup$

Perhaps I've misunderstood something due to oversimplification of the example, but wouldn't it be better to have the precomputed constants explicitly inside the Function returned by f2 rather than using leaked Module variables (a$1667 etc) to store the values? e.g.

f2[a1_, a2_, a3_] := Module[{a = a1, phi = a3}, Function[Evaluate[a a2[phi #1]]]]

l = Cases[s, amp_ trig_[afreq_ t] :> f2[amp, trig, afreq]]
(* {10 Cos[π #1] &, -4 Sin[0.23 #1] &} *)

Through[l[0.33]]
(* {5.09041, -0.303309} *)
$\endgroup$
3
  • $\begingroup$ Well, it's better when you know what you're doing, which does not seem to be my case! $\endgroup$
    – Ramiro
    Jun 6, 2014 at 20:27
  • $\begingroup$ I was super concerned about returning local variables, because I didn't know how they were allocated. They seem to be just hidden global variables, though, no? I did it like that because I wrote the inner function first, thought it was quite perfect, and didn't think about messing it up in any way :) Your approach is a lot better, though. Many thanks. $\endgroup$
    – Ramiro
    Jun 6, 2014 at 20:41
  • $\begingroup$ @Ramiro, yes they're global symbols. Sometimes it's very useful to use Module symbols like that, it's not necessarily a bad thing. You just have to wary of leaking memory if the Module is evaluated many times because it will create a new unique symbol each time. In this case it was more just that there was no need to use symbolic parameters in the Function when the numerical values were already known. $\endgroup$ Jun 6, 2014 at 21:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.