5
$\begingroup$

Suppose I have a function:

fun[n_] := N[2 n*Sin[n]]

When I use it for the following calculations, it gives me some results:

In[2]:= fun[3]
Out[2]= 0.84672

In[3]:= fun[19]
Out[3]= 5.69533

In[4]:= fun[4]
Out[4]= -6.05442

I want my fun to have the extra feature that it can remember its previous results, so that

funResult

{0.84672,5.69533,-6.05442}

and

funRunningTimes

3

fun may include the variables funResult and funRunningsTimes.


This post is related, but the answer provided by Kuba is not satisfactory for this new, more general problem.

$\endgroup$
6
$\begingroup$

You may use DownValues and Memoisation to save and collect previous calls.

getSavedCalls[s_Symbol] :=
 MapAt[ReleaseHold[ReplaceAll[HoldPattern[s[p__]] :> {p}]@#] &, 
  List @@@ Most@DownValues[s], {All, 1}]

getSavedCalls takes the symbol of a memoised function and returns both the parameters and the value of the function with those parameters.

With

fun[n_] := fun[n] = N[2 n*Sin[n]]

and evaluating

fun[2] 
3.63719
fun[-1]
1.68294

Then

getSavedCalls[fun]
{{{-1}, 1.68294}, {{2}, 3.63719}}

The number of saved calls is, of course, the Length of the result getSavedCalls; Length@getSavedCalls[fun]. This isn't the number of function calls but I have a feeling it may be the result you are looking for.

Hope this helps.

$\endgroup$
  • $\begingroup$ Thanks very much.It's a very constructive answer. :) $\endgroup$ – yode Mar 2 '17 at 11:54
2
$\begingroup$

I am not sure why you want to do this so I cannot target my solution to your specific needs.

However I would prefer to keep the main definition as simple as possible for the sake of both readability and ease of future modification, programmatic or otherwise.

A possible complication is the question of what constitutes a "run" of your fun. For example if the function is given malformed input that does not match any definition is that still a run? Since you have not provided context for the problem I shall not delve into that matter at this time.

In the simplest form I therefore propose:

funRunningTimes = 0;

funResult = {};

fun[n_] := ifun[n] // (++funRunningTimes; AppendTo[funResult, #]; #) &

ifun[n_] := N[2 n*Sin[n]]

Following the naming convention of many built-ins I use ifun for the internal fun function.

Test:

fun[3];
fun[19];
fun[4];

funRunningTimes

funResult
3

{0.84672, 5.69533, -6.05442}

Beware: AppendTo is a known slow operation and you would do well to use a different format such as a linked list if you want your function to perform well when accumulating many results.

I chose not to use memoization as Edmund did as that is not an entirely general method, e.g. if your function involves randomness, or otherwise will not always produce the same output for a given input.

$\endgroup$
2
$\begingroup$

For built-in functions we may use the Villegas-Gayley trick:

TanhBag = Internal`Bag[];

Unprotect[Tanh];
Tanh[args___] := Block[{$inMsg = True, result},
   result = N[Tanh[args]];
   Internal`StuffBag[TanhBag, result];
   result] /; ! TrueQ[$inMsg]
Protect[Tanh];

TanhBagLength := Internal`BagLength[TanhBag]
TanhBagContent := Internal`BagPart[TanhBag, All]
TanhBagReset := TanhBag = Internal`Bag[]

Example:

TanhBagReset;
Tanh[0.1]; Tanh[0.2]; Tanh[0.3];
{TanhBagContent, TanhBagLength}

{{0.099668, 0.197375, 0.291313}, 3}

I am using bags, introduced here, as it is a more efficient data type to be used in this manner than List. Using a list and AppendTo may cause performance issues. Bags, on the other hand, is the data type which is used to implement Reap/Sow and it is very suitable for this related purpose.

This solution has the nice property that we can define funResult and funRunningTimes (to use your terminology) without changing the definition of fun. Doing the same for

fun[n_] := N[2 n*Sin[n]]

is actually more complicated than was the case for built-in functions though. The Villegas-Gayley trick relies on the fact that built-in functions use a different context than user-defined functions. The user-defined wrapper function takes precedence in the evaluation order. A few different ways to solve this for user-defined functions are discussed here.

I think that, for the reasons Leonid discusses in the other Q&A, it should be safe to use Villegas-Gayley if Villegas-Gayley is evaluated before the function definition. If it is evaluated after the function definition, I think that

DownValues[fun] = RotateRight[DownValues[fun]];

should generally work, since the pattern in Villegas-Gayley is designed to be as general as possible, which in turn makes Mathematica put it as far down in the evaluation order as possible. (See Leonid's answer for more on this.)

Example:

funBagLength := Internal`BagLength[funBag]
funBagContent := Internal`BagPart[funBag, All]
funBagReset := funBag = Internal`Bag[]

Clear[fun]
fun[n_] := N[2 n*Sin[n]]

fun[args___] := Block[{$inMsg = True, result},
   result = fun[args];
   Internal`StuffBag[funBag, result];
   result] /; ! TrueQ[$inMsg]

DownValues[fun] = RotateRight[DownValues[fun]];

Now run it:

funBagReset;
fun[0.1]; fun[0.2]; fun[0.3];
{funBagContent, funBagLength}

{{0.0199667, 0.0794677, 0.177312}, 3}

$\endgroup$
  • $\begingroup$ I'm sorry,I need some time to understand what you have writed... $\endgroup$ – yode Mar 5 '17 at 9:22
1
$\begingroup$

Simple version to implement it by UpValue

SetAttributes[fun, HoldFirst]

fun[n_] := Module[{}, If[UpValues[Result] == {}, fun[Result] ^= {}];
  First[{Last[fun[Result] ^= Append[fun[Result], N[2 n*Sin[n]]]], 
    fun[RunningTimes] ^= Length[fun[Result]]}]]

Usage

fun[3]
(* 0.84672 *)

fun[16]
(* -9.21291 *)

fun[4]
(* -6.05442 *)

fun[RunningTimes]
(* 3 *)

fun[Result]
(* {0.84672,-9.21291,-6.05442} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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