21. Function Calls:  Problems

Last revision:  April 15, 1997

Author: YourNameGoesHere

saveTranspose

The Mathematica function "Transpose[]" takes a two-dimensional matrix and returns a matrix with the dimensions interchanged (so if the previous size was "2,3", the new matrix has size "3,2") such that the element at array indices "i,j" is the element of the old matrix at array indices "j,i".  Without using "Transpose[]", write your own function to perform the same operation, except that you save the result back in the parameter you are passed using call-by-name.  You do not need to mimic the action of "Transpose[]" on two arguments.

Exchanging Two Columns

Suppose you have two matrices (two-dimensional arrays) which have the same number of rows, and we wish to exchange column "i" from the first matrix with column "j" of the second.  Assume you are given two matrices as arguments together with the additional parameters "i" and "j"; perform the operation, using call-by-name to place the results back in the original parameters.  (Don't use the Mathematica function "Transpose[]"; use your own if needed.)

Swap

Define a function which takes two names "a" and "b" and replaces "a" with the value of "b", "b" with the value of "a".  Use call-by-name.  Note:  make sure that you assign values to "a" and "b" before you execute your swap function on them; for instance, execute "a=1;b=2;" beforehand so that there will be values, and not just names, to swap.

Counting

Evaluate the definition of makeCounter.  We'll talk about it more in a later lesson (hope, hope).

Clear[makeCounter];
makeCounter::usage="makeCounter[] returns a message-driven function
which manipulates a \"hidden\" counter variable.  The initial value
of the counter variable may be set via an optional argument to
makeCounter (default is 0).  Valid messages for the function are
\"++\" (increment), \"--\" (decrement), \"value\", and \"reset\".
For example, ctr = makeCounter[100]; ctr[\"value\"] returns 100.";

makeCounter[
  defaultValue_Integer:0
] := 
  Module[
    { counter = defaultValue,
      changeCounter
    },
    changeCounter[msg_String:"++"] :=
      Switch[msg, 
        "++", 
          counter = counter + 1; counter, 
        "--", 
          counter = counter - 1; counter,
        "value",
          counter,
        "reset",
          counter = defaultValue; counter,
        _,
          Print["Unknown message to counter:", msg];
          counter
      ];
    changeCounter
  ];

?makeCounter

Now consider a functions triplicate and triplicateH.  Both functions return a list of length three, each entry of which is a copy of the argument.

Clear[c1, c2];
ctr1 = makeCounter[]; c1 := ctr1["++"];
ctr2 = makeCounter[100]; c2 := ctr2["++"];

Clear[triplicateH,triplicate];
triplicate[x_] := {x,x,x};

SetAttributes[triplicateH, HoldAll];
triplicateH[x_Symbol] := {x,x,x};

Evaluate the following two calls to these functions.

triplicate[c1]

triplicateH[c2]

Explain the how this example distinguishes between call-by-value and call-by-name evaluation.

Extra Credit: Wasting time.

Define a function whose evaluation takes longer when implemented using call-by-name than it does when implemented using call-by-value.  

(Your function doesn't have to do anything "meaningful" -- it can just waste time on spurious computation.  Provide two versions, waste[] and wasteH[], and show timing results which demonstrate that the "held" version is slower.  Explain why this is so in your example.)