Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I am fairly new to Mathematica (version 7), so please bear with me...

I have duplicated, in Mathematica, a program that I originally wrote in C (it generates OEIS sequence A191837). The problem is that each time I make a change to the function nCk the first evaluation doesn't print. I have to stop the evaluation and then start it again to see the result of my change. What am I missing?

Also, does anyone have any tips on how to make the code more compact?

For[smnds = 4; m = 6, smnds <= 60, m += 6,
  s = {};
  sum = smndct = pct = 0;
  For[p = 5, p < m/2, p += 2,
   If[PrimeQ[p] && PrimeQ[m - p],
    If[sum + p > m, Break[]];
    If[smndct++ < smnds - 1, sum += p];
    AppendTo[s, p];
    pct++]];
  If[pct >= smnds, sdf = Range[smnds];
   While[nCk[Length[s], smnds]]]
  ];
(* Subsets[] uses too much memory, so process each 
   combination one at a time. *) 
nCk[vy_, vz_] :=
  Block[{indx = ns = vy, i = rs = vz, ct = tot = 0},
   If[(sdf[[1]] - 1) == (ns - rs),
    For[ct = 1, ct <= rs, ct++, tot += s[[sdf[[ct]]]]];
    If[tot == m, Print["a(", smnds/2, ") = ", tot];
     smnds += 2; m -= 6; Return[False], Return[False]]];
   While[sdf[[i]] == indx && i > 1, --i; --indx];
   sdf[[i]] += 1;
   i++;
   While[i <= rs, sdf[[i]] = sdf[[i - 1]] + 1; ++i;];
   For[ct = 1, ct <= rs, ct++, tot += s[[sdf[[ct]]]];
    If[tot > m, Break[]]];
   If[tot == m, Print["a(", smnds/2, ") = ", tot];
    smnds += 2; m -= 6; Return[False]];
   Return[True]
   ];
share|improve this question
1  
Jonathan, welcome to Mathematica.SE! Please consider registering your account so that any upvotes you get on this question are added to those you might get on future questions and answers. That way, over time you will be able to do more on the site (post graphics, edit things, etc). –  Sjoerd C. de Vries Sep 7 '12 at 19:41
    
I can finally add a comment! (Firefox was giving me headaches) Thanks to all for the thoughtful answers. –  jnthn Sep 9 '12 at 8:28
add comment

3 Answers

up vote 7 down vote accepted

Invert the order of the For loop and the nCk[] function definition.

You are trying to use the function before defining it.

share|improve this answer
add comment

Others have explained your specific difficulty but you did also ask how to make the code more compact/concise. The short answer is "Say no to loops!" (at least to the extent possible). Below is a partial, longer, answer.

There are several "C-isms" in your code that definitely could be made more concise. You use m, sdf and s in the definition of your function, but don't define them until you reach the For loop. It's a self-referential tangle, and being monolingual in Mathematica, I didn't know that one could even do this in languages like C.

I don't have time to do a full-scale refactor of your code, but here are some useful tips.

First, the For loops. Have a look at the answers to this question, but more specifically, consider, for example this line from your nCk definition:

For[ct = 1, ct <= rs, ct++, tot += s[[sdf[[ct]]]]]

You are just taking the sum of some parts of s. Pretending I know nothing about s or sdf, I could still simplify this as:

tot = Plus@@ s[[Take[sdf,vz] ]]

You are just taking the parts 1 to rs of sdf and using those elements to define the parts of s to use to create the summation tot. Of course, looking at the definitions of sdf and s, they just seem to be Ranges anyway, so there are further simplifications you could try.

Another thing that makes your code more complicated than it needs to be is the double-Sets inside the Block: indx = ns = vy, i = rs = vz. You don't then redefine ns or rs in the rest of the function definition, so why not just leave them out and use the vy and vz parameters passed to the function.

Here's another piece I don't understand: you have this definition in the nCr function:

While[sdf[[i]] == indx && i > 1, --i; --indx]

But when you define sdf just before invoking nCr in the main loop, all it is is a Range. At this point in nCr it hasn't been altered, so you are checking if the $n$th element in a Range vector equals $n$, which definitionally it will. You then get down to i==2, increment sdf[[2]] by 1 (so it's now 3), and increment i by 1, so i should now equal 3, at least if I'm following your code.

Here is the next line:

While[i <= vz, sdf[[i]] = sdf[[i - 1]] + 1; ++i;]

The first time this is invoked, isn't this just equivalent to incrementing the second through vsth elemnts of sdf by 1? You could almost certainly do this without a loop.

Finally, back to the main loop. Try putting a Print[s]; after the AppendTo command, and then re-run the loop. I think you will find a whole bunch of excessive looping and redefining of s.

I hope this is useful. Certainly, programming in Mathematica is very different to many other languages. It is one of the reasons that people coming from other languages find the learning curve so steep. But it is a very elegant language and in many ways can produce more readable code. Sure, pure function Slot (#) notation and some of the syntax forms can take some getting used to. But in my view, it is a darn sight easier to read than a bunch of nested For and While loops.

share|improve this answer
2  
I found it useful - these full and well-written explanations are really helpful for beginners. –  cormullion Sep 8 '12 at 7:15
1  
Thanks @cormullion - these are actually my favorite kind of answer to write. –  Verbeia Sep 8 '12 at 7:20
2  
c is governed by the rule: the user (i.e. programmer) is always right. Another fun phrase: c allows you to easily shoot yourself in the foot, c++ makes it harder, but when you do shoot yourself, you blow your entire leg off. For the most part, c is fairly low-level, and being procedural has a very different feel than mma. Also, the OPs code is definitely from someone used to having to specify every small little detail, including the array indexing. I tend to use c++'s generic programming to reduce that in my code, but it is very different from mma. –  rcollyer Sep 8 '12 at 14:28
add comment

As belisarius already wrote, your problem is that you use the function before you define it. However I think there should be some further explanation.

In most programming languages, function declarations are a compile-time feature. That is, during compilation (and yes, even languages like Perl or Python have a compilation step, it's just done right before execution), the the implementation scans for function definitions and compiles those functions; in most languages it then doesn't matter where the function is defined (some need it to be pre-declared, but very few need to have seen the definition).

However that's not how Mathematica works. In Mathematica, the code is executed strictly from top to bottom. So when the For loop is defined, Mathematica hasn't even yet seen that a definition for nCk follows.

So now you might ask, why did it not complain about the missing function? That's a second way Mathematica differs from most conventional programming languages. Since one of its main areas is symbolic calculations, it is essential that it can handle expressions with functions and values which have not been assigned. This is what enables it e.g. to calculate that the derivative of f[x] g[x] is g[x] f'[x] + f[x] g'[x] instead of complaining that f, g and x are undefined. So when it tries to evaluate nCk[Length[s], smnds], it actually does the following:

  • First, it looks whether nCk has been assigned a value. It hasn't been, therefore nCk evaluates to itself.
  • Then it evaluates Length[s], which (after a few steps) gives the length of the list which was previously assigned to your code (e.g., initially the empty list, so at that point Length[s] evaluates to 0)
  • Then it evaluates smnds (which in the first step has been assigned the value 4).
  • Finally it looks whether there's a definition which matches nCk[0,4]. Since there isn't one (remember, the definition hasn't yet been executed!), this just evaluates to itself.

Now the whole thing is used as control of a While. Now While loops as long as the expression evaluates to True. But your expression does not evaluate to True, it evaluates to nCk[0,4]. Therefore the While loop exits immediately. Note that there's again is no error for arbitrary expressions; the expression is just treated as if it were False (actually If can take a third branch to deal with expressions which are neither True nor False; While doesn't, however). Also, since your For loop doesn't contain any Print statement, it doesn't print anything.

Then after the for loop is executed, Mathematica reaches the definition for nCk[vy_, vz_]. Executing that associates the stuff to the right of the := with the pattern nCk[vy_,vz_] so that from now on anything matching that pattern evaluates to the right hand side (with vy and vz set accordingly). Therefore if you now again execute the for loop, the definition of nCk is used.

Note that you can also change the definition later (this is another point where Mathematica differs from many programming languages). For example, consider the following code:

In[1]:= f[3]
Out[1]= f[3]
In[2]:= f[x_] := x
In[3]:= f[3]
Out[3]= 3
In[4]:= f[x_] := x^2
In[5]:= f[3]
Out[4]= 9
In[6]:= f[3] := 7
In[7]:= f[3]
Out[7]= 7
In[8]:= f[4]
Out[8]= 16
In[9]:= f[3,3]
Out[9]= f[3,3]

You see, at In[1] the function is not yet defined, therefore f[3] just evaluates to itself (without any error). At In[2] it gets a definition, which is used to evaluate In[3]. At In[4] the definition gets replaced, therefore at In[5] the new definition (f[x_]:=x^2) gets used. At In[6], another definition is added, which applies only to the value 3. In In[7] this new definition is used, while in In[8] the previous definition gets used (because f[4] doesn't match f[3], but it does match f[x_] for x==4). Finally, In[9] demonstrates that using an expression which doesn't match the definition(s) still isn't an error, but just continues to evaluate to itself.

Note that the possibility of having several definitions side by side can bite you when making a new definition while the old one still exists; if your expression happens to use the old one to evaluate (which may have long been deleted from your notebook!) then unexpected stuff may happen. This can be avoided by usng ClearAll[symbol] before making your definition (e.g. ClearAll[nCk] before defining nCk).

So in short:

  • Mathematica definitions are statements, which have an effect only when evaluated.
  • Evaluation of a "function call" expression where no suitable definition is available is not an error, but the expression just evaluates to itself.
  • The definitions keep in effect as soon as they are made, unless you explicitly change or delete them. This is why your code works on the second call.
  • Definitions can be replaced, and several definitions can be in effect at the same time. Therefore it is always a good idea to clear any possible leftover definitions for a name before making a new one, using ClearAll.
share|improve this answer
    
I love answers that have explanations as comprehensive as this. –  cormullion Sep 8 '12 at 7:14
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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