Problem
You need to execute some JSL more than once.
Solution
Use a for(...) or while(...) or one of the more exotic variations.
The
for loop is the most common way to repeat some statements.
Cars = {"Ford", "Chevy", "Tesla"}; // a list
nCars = N Items( Cars );
// notice loop starts at one and <= counts to the end
For( iCar = 1, iCar <= nCars, iCar += 1,
car = Cars[iCar]; // JSL lists are 1-based
Write( "\!n", car, " has four wheels." );
);
Ford has four wheels.
Chevy has four wheels.
Tesla has four wheels.
The for loop with real numbers might not do what you expect; numbers like 0.1 are repeating binary fractions that are either slightly too small or slightly too big when truncated to a 64-bit double precision number. In this example, the final value that is very close to 3.0 is actually a bit large.
for(i=2, i<= 3, i+=.1, print(i);); // 2, 2.1, ... 2.9 (but not 3.0)
write("\!nFinally ",i-3); // 8.88178419700125e-16
22.12.22.32.42.52.62.72.82.9Finally 8.88178419700125e-16
If you know you need an integer number of steps, use integers for the for statement and calculate the floating point value from them.
for(i=0,i<=10,i++,
write(" ", 2 + i/10);
// 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3
);
2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3
The
while loop is simpler, but maybe less common, than the for loop. This example would be easier to read with a
for loop; the
next example is a better use of
while.
authors = {"Margaret Mitchell", "J.D. Salinger", "Emily Brontë"};
iAuthor = 1;
While( iAuthor <= N Items( authors ),
Write( "\!n", authors[iAuthor], " wrote one novel" );
iAuthor += 1;
);
Margaret Mitchell wrote one novelJ.D. Salinger wrote one novelEmily Brontë wrote one novel
The while loop can work with a list by removing elements until the list is empty.
animals = {"octopus", "spider", "scorpion"};
While( N Items( animals ) > 0,
// RemoveFrom modifies the animals list
// and returns a list of (in this example)
// one item
animal = Remove From( animals, 1 );
// subscript the list to get just the animal
// name, without the curly braces for the list
Write( "\!n", animal[1], " has eight legs" );
);
octopus has eight legs
spider has eight legs
scorpion has eight legs
For and
While loops can use the
break() statement to break out of the loop early and the
continue() statement to skip the remaining statements and continue at the top. Continue() works best with a for loop that has the increment at the top.
For( i = 1, i <= 10, i++,
If( i == 3, Continue() );
If( i == 7, Break() );
Write( " ", i ); // 1 2 4 5 6
);
1 2 4 5 6
ForEachRow works with a data table and is a shortcut that can be done other ways. The main problem with ForEachRow is that it works with the current data table. Because of this I don't recommend using it if there is more than one table open, and I definitely don't recommend trying to nest ForEachRow loops because the inner loop and the outer loop will confuse each other about the current row.
dt = Open( "$sample_data/big class.jmp" );
For Each Row(
If( Starts With( dt:name, "A" ),
Write( " ", dt:name ); // ALICE ALFRED AMY
)
);
ALICE ALFRED AMY
The formula column loop is done internally in JMP. When you create a formula column in a data table, JMP evaluates the formula for each row. Later, when you change a value in another column that the formula depends on, JMP will re-evaluate rows that depend on the changed value. JMP does these evaluations during idle time. If you want JMP to do them sooner and faster, send the data table a <<RunFormulas message.
dt = Open( "$sample_data/big class.jmp" );
hw = dt << New Column( "HW_Ratio", formula( height / weight ) );
dt << RunFormulas; // all 40 values are computed
dt << Sort(by (HW_Ratio), replaceTable); // tall enough for my weight?
Table with formula column
Many functions take a matrix argument and apply the function to every element. A matrix of results is returned.
s = Round(Sqrt( [1, 2, 3, 4, 5, 6, 7, 8, 9] ), 2);
write(s); // [1, 1.41, 1.73, 2, 2.24, 2.45, 2.65, 2.83, 3]
The matrix function
Loc loops through a matrix and returns the locations of elements that Loc identifies. This is much faster than a for loop.
s = Round(Sqrt( [1, 2, 3, 4, 5, 6, 7, 8, 9] ), 2);
write(Loc(s > 2)); // [5, 6, 7, 8, 9] (element 4 is equal)
Using
matrix subscripting can reorganize matrices much faster than for loops.
s = Round( Sqrt( [1, 2, 3, 4, 5, 6, 7, 8, 9] ), 2 );
s[Loc( s > 2 )] = -1;
Write( s ); // [1, 1.41, 1.73, 2, -1, -1, -1, -1, -1]
Everything beyond this point is an unusual usage pattern that should be avoided unless you've got a really good reason. It will be hard to understand later and very hard for someone else to maintain.
The
J function creates a matrix and initializes it with a value. If that value is an expression, it is evaluated for every element. In this example, the third argument to J() is a pair of statements in parens; the first statement increments a counter and the second statement calculates a value. The last value calculated is the value of the expression made of several statements.
idx = 0; // not a recommended pattern!
mat = J( 2, 3, (idx += 1 ; Sqrt( idx )) );
Write( Round( mat, 1 ) ); // [1 1.4 1.7, 2 2.2 2.4]
The
parallel assign function uses multiple threads to fill in an array. Unlike the J function 3rd argument, you can't depend on any particular ordering of the elements being calculated. Parallel assign tries to keep threads isolated from each other and really doesn't want its expression accessing global data.
bigMat = J( 500, 500, . );
// these are global x and y values that are only copied into each thread, below.
x = 2;
yy = 3;
// x=x and y=yy is copying global values to local values of the same/similar name.
// bigMat[rr, cc] determines the array that is being filled in and the
// row and column names you'll use in the expression after the =.
// the if statement is an example expression, not terribly interesting but
// typical of the kind of thing parallel assign does well.
rc = Parallel Assign( {x = x, y = yy}, bigMat[rr, cc] = If( x == 2, rr * cc, y ) );
// Max dose NOT return an array, it returns the maximum value in the array.
// dividing by max scales the values from 0 to 1
bigMat = bigMat / Max( bigMat );
// new image expects values from 0 to 1
New Window( "demo", New Image( "rgb", {bigMat, 1 - bigMat, bigMat} ) );
250,000 if statements evaluatedThe
pattern matcher can loop over a string and run your JSL as the match progresses.
text = // use the \[ and ]\ to escape the quotes in this text
"\[John Bartlett, who ran the University Book Store in Cambridge,
Massachusetts, was frequently asked for information on quotations and
he began a commonplace book of them for reference. In 1855, he
privately printed his compilation as A Collection of Familiar Quotations.
This first edition contained 258 pages of quotations by 169 authors,
chiefly the Bible, William Shakespeare, and the great English poets.
Bartlett wrote in the fourth edition that "it is not easy to determine in
all cases the degree of familiarity that may belong to phrases and
sentences which present themselves for admission; for what is familiar
to one class of readers may be quite new to another." - Wikipedia]\";
words = [=> 0]; // an associative array that defaults unknown keys to a value of 0
// this match repeats a pair of alternatives: a run of a-z or a run of NOT a-z.
// the runs of a-z are stored in an associative array
rc = Pat Match(
text,
Pat Repeat( // the loop starts here
(Pat Regex( "[a-zA-Z]+" ) >> word
+Pat Test( // insert word into words assoc array
words[Lowercase( word )] += 1; // this JSL is evaluated for each word
1; // the test must succeed for the pattern to continue
)) | Pat Regex( "[^a-zA-Z]+" ) // skip non-words
)
);
// make a table, load it from the words assoc array keys and values
dt = New Table( "word count",
New Column( "word", character, Set Values( words << getkeys ) ),
New Column( "count", Set Values( words << getvalues ) )
);
// sort by the count column
dt << sort( by( count ), order( descending ), replaceTable );
Table of word counts
The
scheduler can run your code again and again, every few seconds.
n = 0; // counter, just for this demo
dowork = Function( {},
Print( "hi", n );
n += 1;
If( n < 10,
Schedule( .5, dowork() );// half second from now, run me again
, // else
Schedule( 0, 0 ) << close; // no delay, no code
);
);
dowork(); // start the loop
Recursion can walk through a tree.
// this tree walker looks for button boxes in a display
// box tree and prints the button name
treeWalker = Function( {box}, // parameter
{child = box << child}, // local variables
While( !Is Empty( child ),
If( child << classname == "ButtonBox",
Show( child << Get Button Name )
);
treeWalker( child ); // you could also use recurse(child)
child = child << sib;
)
);
x = V List Box(
H List Box( Button Box( "x" ), Button Box( "z" ) ),
Border Box( Button Box( "w" ) )
);
treeWalker( x ); // shows x, z, and w
child << Get Button Name = "x";
child << Get Button Name = "z";
child << Get Button Name = "w";
Discussion
Nesting loops can get quite slow. If you write an outer loop that examines every row in a table, and an inner loop that also examines every row in the table (perhaps looking for matches), JSL for loops will get painfully slow when the table has a 100,000 rows: the JSL in the inner loop will run 100,000 * 100,000 (10,000,000,000) times. If you can move this code into a matrix function, it will be faster, but you might need a different approach, sorting the table perhaps.
I've probably overlooked something.
See Also
http://www.jmp.com/support/help/Iterate.shtml
http://www.jmp.com/support/help/Conditional_and_Logical_Functions.shtml
and many other articles within the community