7

I am new to Mathematica, and using a Module to perform a procedure, then return a value. However, Mathematica seems to be evaluating and returning symbolic values instead of the numerical value I want.

Questions I have are: When don't you use semicolons? And when do you use Return[value] instead of just writing "value"?

DumpVar[x_] := Print[ToString[HoldForm[x]], ":", x];
SetAttributes[DumpVar, {Listable, HoldAll}]

width = 1;
interval = width/2;

panelCoeff = 2;
lightAngle = Pi/3;

(*Panel and light equations*)

panel[x_] = Abs[panelCoeff x];(*panelCoeff ((x)^2);*)

light[x_] = Tan[lightAngle]*x;

getAngleAttack[offset_] := 
  Module[{bounce1x, l1a, lightSlope, panelSlope},
   light[x_] = light'[x] (x - offset) + panel[interval];
   DumpVar[offset];

   lightSlope = N[light'[offset]];
   u1S = light'[offset];
   u1[x_] = (u1S (x - offset)) + panel[interval]; 

   bounce1x = 
    x /. N[NSolve[u1[x] == panel[x] && x < interval && x > -interval, 
       x]];

   u1r[x_] = panel'[bounce1x] (x - bounce1x) + panel[bounce1x];

   If[Length[bounce1x] > 0,
     bounce1x = bounce1x[[1]];,
     bounce1x = offset;
     ]

    If[bounce1x > -interval && bounce1x < interval,

     lightSlope = N[u1'[bounce1x]];

     If[x <= 0,
      panelSlope := N[panelCoeff],
      panelSlope := -N[panelCoeff]];

     DumpVar[lightSlope];
     DumpVar[panelSlope];
     l1a = 
      N[ArcTan[(lightSlope - 
           panelSlope)/(1 + (panelSlope lightSlope))]];

     DumpVar[l1a];

     l1a
      Return[l1a]
     ]

    Return[l1a];
   ];


myint = getAngleAttack[0];
(*myint = N[f[10]];*)
DumpVar[myint];

Plot[{panel[x], light[x]}, {x, -.6, .6}]

myint = getAngleAttack[.5];
DumpVar[myint];

My goal is to be able to graph and integrate this function.

Geekgirl
  • 1,322
  • 3
  • 11
  • 17
  • I have to work through this, but be careful in how you name your variables. It's not obvious what you mean by `light[x_] = light'[x] (x - offset) + panel[interval]` – Mike Bailey Nov 15 '11 at 16:50
  • @yoda: There's a few other issues too. Further down in the module is a bunch of problems. I'll post an answer with some notes. – Mike Bailey Nov 15 '11 at 16:56
  • I've added some additional notes to further simplify/explain some points. – rcollyer Nov 15 '11 at 18:02

2 Answers2

10

In the middle of your block you have:

If[Length[bounce1x] > 0,
  bounce1x = bounce1x[[1]];,
  bounce1x = offset;
  ]

The format of If is as follows: If[Condition, ValueIfTrue, ValueIfFalse]

So If[True, 3, 2] returns 3, and If[False, 3, 2] returns 2. Your semicolons here are unnecessary, but you do need one at the end of the if statement:

If[Length[bounce1x] > 0,
  bounce1x = bounce1x[[1]],
  bounce1x = offset
  ];

Otherwise, Mathematica will interpret that as multiplication of that statement times whatever the next one to show up will be. In this case, you're returning null from that If statement, and it's being multiplied by the return value of the next If statement that comes up.

For Module the syntax is: Module[{localvars}, ReturnValue]

Which means whatever is the last statement that shows up without a semicolon is the ReturnValue. So for example the following module:

Module[{y},
   y = x * x;
   If[x < 0, -y, +y]
]

will return -y when x < 0, and +y otherwise. The one exception this is when Return shows up. Just like in most languages, you can return early from a function by using Return:

Module[{y},
   y = x * x;
   If[x < 0, 
      Return[-y],
      Return[+y]];
 (* We never reach this point to return null *)
 ];

With respect to your Module, I think this may be what you're trying to accomplish:

getAngleAttack[offset_] := 
 Module[{bounce1x, l1a, lightSlope, panelSlope}, 
  light[x_] = light'[x] (x - offset) + panel[interval];
  DumpVar[offset];

  lightSlope = N[light'[offset]];
  u1S = light'[offset];
  u1[x_] = (u1S (x - offset)) + panel[interval];

  bounce1x = 
   x /. N[NSolve[u1[x] == panel[x] && x < interval && x > -interval, 
      x]];

  u1r[x_] = panel'[bounce1x] (x - bounce1x) + panel[bounce1x];

  If[Length[bounce1x] > 0,
   bounce1x = bounce1x[[1]],
   bounce1x = offset];

   If[bounce1x > -interval && bounce1x < interval,

   lightSlope = N[u1'[bounce1x]];

   If[x <= 0,
    panelSlope := N[panelCoeff],
    panelSlope := -N[panelCoeff]];

   DumpVar[lightSlope];
   DumpVar[panelSlope];

   l1a = N[
     ArcTan[(lightSlope - panelSlope)/(1 + (panelSlope lightSlope))]];

   DumpVar[l1a];
   Return[l1a]
  ];
  l1a]

Another thing you should watch out for is any variables you use inside of a Module. If you run the following piece of code, you'll get 4, -113/5, 32 as the output values:

d = 4 (* d was 4 *)
Module[{a, b, c},
   a = 3;
   b = 2;
   c = 5;
   d = 32; (* Uh oh! I just overwrite whatever d was *)
   a^2 + b / c - d]
d (* d is now 32 *)

To avoid this, define any variables you're using as local variables within the start of the Module: Module[{a, b, c, d}, ...]

Mike Bailey
  • 12,479
  • 14
  • 66
  • 123
  • Thanks! I was having trouble figuring out that syntax, especially coming from programming languages where semicolons are in EVERY line. :-) – Geekgirl Nov 15 '11 at 17:10
  • The semicolons here are necessary if you want to do something but NOT return a value. The semantics of semicolons in Mathematica are to suppress the return value of a statement, and instead return `null`. So in a sense, you *do* need semicolons on almost every line. – Mike Bailey Nov 15 '11 at 17:13
  • 3
    @Geekgirl Perhaps now is a good time to tell that ';' is actually -just like everything else in Mathematica- a function. In this case the function `CompoundExpression`,with `a;b` being equivalent to `CompoundExpression[a,b]` and `a;b;` equivalent to `CompoundExpression[a,b,Null]` – Sjoerd C. de Vries Nov 15 '11 at 17:48
  • @SjoerdC.deVries, beat me to it. I wrote a more lengthy answer that included that. – rcollyer Nov 15 '11 at 18:03
8

I'd like to add a couple of things to Mike's excellent answer.

First, semi-colons are way of building compound expressions, and as Mike pointed out, they suppress the output of the immediately preceding statement. But, they're most useful in allowing you to chain multiple expressions together where only a single expression is expected, like in the body of a Module, as you're doing. However, they're useful for simpler things like this contrived example

a = 5;
b = (Print[a]; a - 3)

Note the parentheses; ; has a lower precedence than =, so

b = Print[a]; a - 3

would set b to the return value of Print which is Null.

To incorporate what Sjoerd was saying in his comment, the expression

b = (Print[a]; a - 3)

is interpreted as

Set[b, CompoundExpression[ Print[a], a - 3] ]

while the second, incorrect, form is interpreted as

CompoundExpression[ Set[b, Print[a]], a - 3]

If you want to see what form an expression takes, use FullForm[Hold[ expression ]] which reveals the internal form of an expression. You need to use Hold when you don't want anything to be executed prior to examining its form, as would be the case with Set.

Second, when using an If expression to Set a variable to different values, it can be pulled out of the If statement, as follows

 bounce1x = If[Length[bounce1x] > 0, bounce1x[[1]], offset];

Since If will return either bounce1x[[1]] or offset. This can greatly simplify your expressions. This also works with SetDelayed (:=), like for panelSlope

panelSlope := If[x <= 0, N[panelCoeff], -N[panelCoeff]];

However, I wouldn't use SetDelayed here as you don't need to recalculate panelSlope every time you use it. Also, you can simplify that a little by using UnitStep,

panelSlope = (1 - 2 UnitStep[x]) N[panelCoeff];

or, even

panelSlope = If[x<=0, 1, -1] N[panelCoeff];

(Sign wouldn't be appropriate here, as it will return zero when its parameter is 0.)

Lastly, there's a bug in your code with regards to l1a. Your Module returns it, but if the bounce1x > -interval && bounce1x < interval condition is not met, the If statement where it is set is not entered. So, it will return something of the form l1a$### where ### are numbers. Also, I'd get rid of the Return[ l1a ] in that If statement entirely, as it is not necessary, l1a is set within the If statement.

Community
  • 1
  • 1
rcollyer
  • 10,475
  • 4
  • 48
  • 75
  • @Sjoerd, sorry, I'm not as familiar with the "o" before "e" rules as I am with the "i" before "e" rules. :) – rcollyer Nov 15 '11 at 19:10
  • Thank you so much for such a helpful and thorough answer! It's exactly the information I was lacking. :-) – Geekgirl Nov 16 '11 at 03:12