0
$\begingroup$

tl;dr: pls fix my spaghetti code

Hi all. I'm a physics undergrad and I work in a lab at my university doing gamma spec. Mostly for fun/learning to program, I'm trying to write a module that reads the ASCII spectra format that my software exports. I've managed to write something that parses the file for the relevant bin numbers/counts, locates and applies the calibration factors, and generates a plot. It works fine (though I'm open to suggestions to make it better), but I've found that accidentally evaluating the cell twice causes a bunch of errors. There's an easy work around (just don't evaluate it again), but I took it as a challenge. My idea was to just use a logic statement, but it seems like its getting ignored completely whenever I hit shift-enter. Here is what I've tried to do (most of the stuff towards the bottom isn't super important; I think whatever's going wrong is at the top):

ReadSpe[FileName_, EnergyIn_] := 
 If[ValueQ[EnergyIn], 
  Print["Please clear the expression " <> ToString[EnergyIn] <> 
" before preceeding with ReadSpe"], doIfFalse[FileName, EnergyIn]]

 (*The idea here is that I want it to abort if the provided expression is 
 already defined*)

doIfFalse[FileName_, EnergyIn_] := (
  Clear[CalIn, DataIn, InFile, Np];
  InFile = OpenRead[FileName];
  CalIn = {};
  DataIn = {};
  EnergyIn = {};
  (*Creates data set "CallIn","DataIn",
   "EnergyIn"and initializes them empty*)
  Do[Read[InFile, String], {i, 1, 11}];
  (*Skips past preamble to the data in.Spe file*)
  Np = Part[Read[InFile, {Number, Number}], 2] + 1;
  (*Picks out the list length that is in the next data line and saves \
   it to "Np"*)
  Do[AppendTo[DataIn, {i - 1, Read[InFile, Number]}], {i, 1, Np}];
  (*Creates a data set with the form {{bin,count},{bin,count},...}*)
  Do[Read[InFile, String], {i, 1, 10}];
  (*Now reads SPE file to find calibration constants*)
  AppendTo[CalIn, Read[InFile, {Number, Number, Number}]];
 Do[AppendTo[
    EnergyIn, {CalIn[[1, 1]] + CalIn[[1, 2]]*DataIn[[i, 1]] + 
    CalIn[[1, 3]]*(DataIn[[i, 1]])^2, DataIn[[i, 2]]}], {i, 1, Np}];
  (*Creates a list of pairs of the form (energy,counts*)
  Close[InFile];
  Clear[CalIn, DataIn, InFile, Np];
  ListPlot[EnergyIn, PlotRange -> All, ImageSize -> Full, 
   AxesLabel -> {HoldForm[Energy[keV]], HoldForm[Counts]}, 
   PlotLabel -> HoldForm[EnergyIn], 
   LabelStyle -> {FontFamily -> "Al Bayan", 12, GrayLevel[0]}, 
   PlotMarkers -> {Automatic, 2}](*Plots data*))

It works fine on the first pass - it generates the plot, and the data is properly appended to EnergyIn_. Running the module again generates the following error:

Set::shape: Lists {{1.4514,0},{1.84485,0},{2.23831,0},{2.63176,0},
{3.02521,0},{3.41866,0},{3.81211,0},{4.20556,0},{4.59901,0},{4.99246,0},
<<32>>,{17.9765,274},{18.3699,185},{18.7634,81},{19.1569,61},{19.5503,58},
{19.9438,52},{20.3372,52},{20.7307,90},<<8142>>} and {} are not the same 
shape.

AppendTo::rvalue: {{1.4514,0},{1.84485,0},{2.23831,0},{2.63176,0},
{3.02521,0},{3.41866,0},{3.81211,0},{4.20556,0},{4.59901,0},{4.99246,0},
<<32>>,{17.9765,274},{18.3699,185},{18.7634,81},{19.1569,61},{19.5503,58},
{19.9438,52},{20.3372,52},{20.7307,90},<<8142>>} is not a variable with a 
value, so its value cannot be changed.

AppendTo::rvalue: {{1.4514,0},{1.84485,0},{2.23831,0},{2.63176,0},
{3.02521,0},{3.41866,0},{3.81211,0},{4.20556,0},{4.59901,0},{4.99246,0},
<<32>>,{17.9765,274},{18.3699,185},{18.7634,81},{19.1569,61},{19.5503,58},
{19.9438,52},{20.3372,52},{20.7307,90},<<8142>>} is not a variable with a 
value, so its value cannot be changed.

AppendTo::rvalue: {{1.4514,0},{1.84485,0},{2.23831,0},{2.63176,0},
{3.02521,0},{3.41866,0},{3.81211,0},{4.20556,0},{4.59901,0},{4.99246,0},
<<32>>,{17.9765,274},{18.3699,185},{18.7634,81},{19.1569,61},{19.5503,58},
{19.9438,52},{20.3372,52},{20.7307,90},<<8142>>} is not a variable with a 
value, so its value cannot be changed.

General::stop: Further output of AppendTo::rvalue will be suppressed during 
this calculation.

Then it just outputs the list as an 8142x2 matrix. Pls help me I know not what I am doing.

$\endgroup$
  • $\begingroup$ TL;DR if you know where stuff is going wrong, why do you simply dump all your "spaghetti code" here and believe this is appealing? Why not at least try to make this a minimal example? $\endgroup$ – gwr May 3 '17 at 6:21
4
$\begingroup$

The problem here is most likely If[ValueQ[x], ...]. Check this out:

test[x_] := ValueQ[x]

Clear[var];
test[var]

(* Out: False *)

var = {1,2,3};
test[var]

(* Out: False *)

var evaluates before it is passed to the function test, so test[var] is equivalent to test[{1,2,3}]. ValueQ[{1, 2, 3}] is False.

This is different from

var = {1, 2, 3};
ValueQ[var]

because in this case var is not evaluated before it is passed to the function, in this case ValueQ. The reason for this is that ValueQ has the HoldAll attribute:

Attributes[ValueQ]
(* Out: {HoldAll, Protected, ReadProtected} *)

What ValueQ answers is the question: "If this expression is evaluated, will its value change?" If you pass a variable such as var to ValueQ then the answer is yes, because var will evaluate to {1, 2, 3}. But if you pass {1, 2, 3} to ValueQ then the answer is no, because {1, 2, 3} evaluates to {1, 2, 3}.

Try SetAttributes[Readspe, HoldRest]. If it does not solve the problem, try rewriting your code in a way that does not require you to assign a value to a variable that you pass into the function. That kind of programming can become quite advanced in Mathematica, it requires a good knowledge of how evaluation works.

The code reminds me of how things are often done in C, where you send a pointer to a function and it modifies the value that the pointer points to. That strategy is not very good in Mathematica, and you will find that you will like the language more if you avoid it.

I would recommend using Module instead of clearing variables at every function call.

$\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.