A recent exchange on the R-sig-teaching list featured a discussion of how best to teach new students R. The initial post included an exercise to write a function, that given a n, will draw n rows of a triangle made up of “*”, noting that for a beginner, this may require two for loops. For example, in pseudo-code:
for i = 1 to n for j = 1 to i print "*"
Unfortunately, as several folks (including Richard M. Heiberger and R. Michael Weylandt) noted, for loops in general are not the best way to take full advantage of R. In this entry, we review two solutions they proposed which fit within the R philosophy.
Richard’s solution uses the outer() function to generate a 5×5 matrix of logical values indicating whether the column number is bigger than the row number. Next the ifelse() function is used to replace TRUE with *.
> ifelse(outer(1:5, 1:5, `>=`), "*", " ") [,1] [,2] [,3] [,4] [,5] [1,] "*" " " " " " " " " [2,] "*" "*" " " " " " " [3,] "*" "*" "*" " " " " [4,] "*" "*" "*" "*" " " [5,] "*" "*" "*" "*" "*"
Michael’s solution uses the lapply() function to call a function repeatedly for different values of n. This returns a list rather than a matrix, but accomplishes the same task.
> lapply(1:5, function(x) cat(rep("*", x), "\n")) * * * * * * * * * * * * * * *
While this exercise is of little practical value, it does illustrate some important points, and provides a far more efficient as well as elegant way of accomplishing the tasks. For those interested in more, another resource is the R Inferno project of Patric Burns.
We demonstrate a SAS data step solution mainly to call out some useful features and cautions. In all likelihood a proc iml matrix-based solution would be more elegant;
data test; array star  $ star1 - star5; do i = 1 to 5; star[i] = "*"; output; end; run; proc print noobs; var star1 - star5; run; star1 star2 star3 star4 star5 * * * * * * * * * * * * * * *
In particular, note the $ in the array statement, which allows the variables to contain characters; by default variables created by an array statement are numeric. In addition, note the reference to a sequentially suffixed list of variables using the single hyphen shortcut; this would help in generalizing to n rows. Finally, note that we were able to avoid a second do loop (SAS’ primary iterative looping syntax) mainly by luck– the most recently generated value of a variable is saved by default. This can cause trouble, in general, but here it keeps all the previous “*”s when moving on to the next row.
An unrelated note about aggregatorsWe love aggregators! Aggregators collect blogs that have similar coverage for the convenience of readers, and for blog authors they offer a way to reach new audiences. SAS and R is aggregated by R-bloggers and PROC-X with our permission, and by at least 2 other aggregating services which have never contacted us. If you read this on an aggregator that does not credit the blogs it incorporates, please come visit us at SAS and R. We answer comments there and offer direct subscriptions if you like our content. In addition, no one is allowed to profit by this work under our license; if you see advertisements on this page, the aggregator is violating the terms by which we publish our work.