Skip to main content
    Country/region select      Terms of use
     Home      Products      Services & solutions      Support & downloads      My account     

developerWorks  >  Lotus  >  Forums & community  >  Best Practice Makes Perfect

Best Practice Makes Perfect

A collaboration with Domino developers about how to do it and how to get it right in Domino

woman pumping electricityAs grandfathers tend to do, mine used to tell stories about how tough they had it when he was a kid. "Why, we used to have to pump our own electricity by hand!" he might say, or "We had no TV in those days, so we just had to sit and watch the wall."

Us old-timers who were developing Notes applications before version 6.0 have our own tales to tell. This was before the days of the macro looping functions @For, @Transform, @While, and @DoWhile, and I'm sorry to say, you young whippersnappers have gotten lazy and are using these new functions when there are perfectly adequate ways to do the same thing without them, based on the fact that most operators and @Functions, if you give them a list, will process every element of that list. So for instance, we might see something like this:

result := "";
@For(i := 1; i <= @Elements(TopSet); i := i + 1;
@If(i =1;
result := @Left(TopSet[i]; ":") + " (" + @Text(TopPrice[i]) + ")";
result := result : (@Left(TopSet[i]; ":") + " (" + @Text(TopPrice[i]) + ")")));

The following formula returns the same result, with much less hubbub:

@Left(TopSet; ":") + " (" + @Text(TopPrice) + ")"

If your boss measures your productivity by lines of code, the first formula is obviously better. But for most of us, our managers don't count lines, and are more concerned that we complete our tasks quickly, that our applications work, and that the performance is acceptable. The second formula is a winner in these three areas. It's less typing, it's far easier to debug (my first version of the first formula failed because I forgot about the relative operator precedence of : vs. +). We have to go through the same loop in either case, but the first formula does the loop in an interpreted language, while the second formula loops in fast C code. Even though the second formula actually has to go thru a loop twice (once while processing @Left, and once while processing @Text), it's no contest which is fastest. The second formula is also easier to maintain because it's obvious at a glance what it does -- at least, it is if you know the macro language.

It's true that for those developers who don't understand lists, the second formula may be a little cryptic at first. "Wait! Doesn't it need to loop through all of them?!?" It's important to remember that these are the people we want to punish anyway for not reading the documentation. It will teach them a lesson and maybe their formulas will be more efficient in the future.

Another nifty feature of formula language that people tend to forget about immediately once they've turned past that page in the beginning development class, are the combinatoric operators *+ and *= (there are permuted versions of other operators also, but most of them actually are pretty useless). It's not every day that you need to operate on every combination of a pair of elements from two lists, but sometimes it's nice. For instance, to tell whether the two lists a and b contain any element in common, you could write:

@Keywords(a; b; "") != ""

but it is tres chic to express it as follows (and it also works for non-text lists):

a *= b

Here's a challenge to exercise your list-processing skills. Write a formula that takes the text list Items and produces as output a list where each element is an element of Items preceded by a consecutive number. E.g. if Items = "cow":"horse":"pig" the output of your formula should be "1. cow":"2. horse":"3. pig". Items may contain up to 500 elements, and you may not use @Transform, @For, @While or @DoWhile. Post your answers as comments if you like, but please, readers, try it yourself before you look at other people's answers. The shortest formula wins (note: there is not an actual prize, just prestige). Whitespace doesn't count towards the length.

Andre Guirard | 3 April 2007 07:56:09 PM ET | Plymouth, MN, USA | Comments (42)


1) Geek Formula Puzzle
Jerry Glover | 4/3/2007 10:40:59 PM

I'll play!

x := @Text(0:1:2:3:4:5:6:7:8:9);

n := @Text(@TextToNumber(@Subset( x; 5) *+ x *+ x )+1);

@Subset( n; @Elements(Items)) + ". " + Items

But I just did something like this on my blog a couple of weeks ago. { Link }

2) Geek Formula Puzzle
Nigel Roulston | 4/4/2007 1:20:12 AM

Here's my effort

items := "cow1":"horse1":"pig1";

BL := "0":"1":"2":"3":"4":"5":"6":"7":"8":"9";

LL := @Subset(BL *+ BL *+ BL; @Elements(items));

@Text(@TextToNumber(LL)+1) + ". " + items;

3) Geek Formula Puzzle
Graeme Walker | 4/4/2007 2:44:35 AM

how about:


@Transform(items; "X"; @Text(@Member(X;items)) + ". " + X )

4) Geek Formula Puzzle
Lars Berntrop-Bos | 4/4/2007 5:34:58 AM


@subset(d*+d*+d; @Elements(Items))+"."+Items

5) Geek Formula Puzzle v1.1
Lars Berntrop-Bos | 4/4/2007 5:38:42 AM


@Subset(@TextToNumber(d*+d*+d)+1; @Elements(Items))+"."+Items

6) Geek Formula Puzzle
Ricardo Lucio | 4/4/2007 6:40:00 AM


max:=@Elements( Items );


@Text(((@TextToTime(@Explode(@TextToTime(@Text(startDate) + "-" +

@Text(@Adjust(startDate; 0; 0; max - min; 0; 0; 0))))) -

startDate)/86400) + min) + "." + Items

7) Geek Formula Puzzle
Kerr Rainey | 4/4/2007 6:43:56 AM

My answer is similar to Graeme's. Mine is 1 char shorter and I think should be faster. Use a transform, you know it makes sense. I didn't peek either ;)


@Transform(Items; "i"; @Do(c:=c+1; @Text(c)+". "+i))

8) Geek Formula Puzzle
Kerr Rainey | 4/4/2007 7:46:09 AM

Looks likeyou don't need the @do.

c:=0; @Transform(Items; "i"; @Text(c:=c+1)+". "+i)

9) Geek Formula Puzzle
Slawek Rogulski | 4/4/2007 9:03:42 AM

@Explode(@Eval(@Implode("@Text("+("@Member( \""+Items + "\" ; Items )")+")";" + \".\" + " ));".")+". " +Items

10) Geek Formula Puzzle
Andre Guirard | 4/4/2007 9:06:07 AM

Perhaps I was unclear; @Transform is one of the looping functions you may not use.

11) Geek Formula Puzzle
Andre Guirard | 4/4/2007 9:13:34 AM

So far I see the following:

Jerry Glover: 113 chars.

Nigel Roulston: 121 chars.

Ricardo Lucio: 194 chars.

The other formulas either use the forbidden @Transform or don't work. Slawek Rogulski made innovative use of @Eval, but his formula fails when the list contains repeated items (besides which, I should've said you can't use @Eval also -- but I didn't, so I'll accept it if he can find a way to make it work).

12) Geek Formula Puzzle
Andre Guirard | 4/4/2007 9:28:34 AM

Come on folks -- it can be made a lot shorter still!

13) Geek Formula Puzzle
Slawek Rogulski | 4/4/2007 10:18:59 AM


@Explode(@Eval(@Implode("@Text("+("@Member( \""+uniqueList + "\" ; uniqueList )")+")";" + \".\" + " ));".")+". " +Items

14) Geek Formula Puzzle
Kerr Rainey | 4/4/2007 10:43:43 AM

Doh!! I never thing of @transform as a "Looping Function", and for some reason I thought it had been around since before v6. Of course it would help if I read the instructions properly.

Lets have another look...

15) re: Geek Formula Puzzle
Andre Guirard | 4/4/2007 10:46:09 AM

Slawek, that works, but it's too long now.

The shortest answer I've come up with does use @Eval. But the *+ formulas can also be done in less than 85 chars.

16) Geek Formula Puzzle
John Smart | 4/4/2007 11:04:58 AM

@Subset(@Text(@TextToNumber(d := @Text(0:1:2:3:4:5:6:7:8:9)) *+ d *+ d) + 1); @Elements(i := Items)) + ". " + i

97 characters, not counting whitespace, but counting the space within ". "

My Barcolounger was reserved in 1984, I'll have you know. ;-)

17) Oooh, Eval!
John Smart | 4/4/2007 11:20:39 AM

I hadn't thought of that Eval and Repeat.

i := 0;

@Eval(@Repeat({(@Text(i := i + 1) + ": " + Items:}; @Elements(Items)) + {""})

74 chars

18) @17 - accidental markup
John Smart | 4/4/2007 11:23:31 AM

Hmm... it appears that the bracket - i - bracket after the first "Items" was interpreted as a markup to italicize the rest.

19) Geek Formula Puzzle
Granvel Ficklin | 4/4/2007 12:11:19 PM

Kind of long, but words w/ no looping formula language.

myList:= "cow":"horse":"pig";

myListCount := @Elements(myList);

maxListCount := 500;

numberSet := "0":"1":"2":"3":"4":"5":"6":"7":"8":"9";

numberList := @Text(@TextToNumber(@Subset(numberSet *+ numberSet *+ numberSet; maxListCount)) + 1);

myListNumbers := @Subset(numberList; myListCount);

REM {concatenate number list with text list.};

myListNumbers + ". " + myList

20) re: Oooh, Eval!
Andre Guirard | 4/4/2007 12:38:06 PM

Sorry, I get an error from this one, inappropriate (unary) usage of operator.

21) re: Geek Formula Puzzle
Andre Guirard | 4/4/2007 12:41:06 PM

Insufficient arguments for @Function.

22) Geek Formula Puzzle
Kerr Rainey | 4/4/2007 1:05:32 PM

Pah, I managed to get the same answer as @16, but using @count instead of @elements, knocking a whole 3 chars off ;) not sure how to get it down to less than 85.

23) Arggh! :-)
Chris Blatnick | 4/4/2007 1:56:16 PM

I essentially got the same thing as Jerry. If you add the bounding condition of no more than 500 entries, I can't cut the formula down to less than about 124 characters. I played by the rules and avoided all functions introduced in R6. I can't wait to see your solution.

Thanks for the mental workout Andre, but I have a question. Can I bill you for the lost time! :-D

24) re: Arggh! :-)
Andre Guirard | 4/4/2007 2:23:06 PM

You can bill anyone for anything -- the question is will they pay it?

25) Geek Formula Puzzle
Nigel Roulston | 4/4/2007 7:26:38 PM

2nd attempt

Ok, after a sleepless night thinking about this.... (ok, not really, but it sounds good)

I got it down to (what I think is) 83 chars, but I don't know if @Sort(R6) is allowed.


@Text(@Sort(@Subset((B*100)*+(B*10)*+B;@Count(i))+1))+". "+i;

26) Geek Formula Puzzle
Brendan Long | 4/4/2007 7:55:54 PM

Stealing some of the ideas above (My original entry would have been the same as Nigel's first one):


@Subset(@Text((a*100)*+(a*10)*+a+1)+". "+b:=Items;@Count(b))

84 characters.

27) Geek Formula Puzzle
Brendan Long | 4/4/2007 7:59:54 PM

Nigel (who is my boss) just reminded me that I don't need the brackets on the top assignment (I guess that's why he's the boss)


@Subset(@Text((a*100)*+(a*10)*+a+1)+". "+b:=Items;@Count(b))

83, including the space in ". "

28) Geek Formula Puzzle
Jerry Glover | 4/4/2007 8:47:51 PM

I was coming back with the improved version and I see Brendan beat me to it with even a slightly better version of what I was going to post as his uses in-line variable assignment. I did eliminate unnecessary parens in mine though - natural operator evaluation order works fine here. Without the extra parens and taking the inline assignment a step further you get it down to 80 chars:

@subset(@text((x:=0:1:2:3:4:5:6:7:8:9)*100*+x*10*+x+1)+". "+i:=items; @count(i))

29) Geek Formula Puzzle
Graeme Walker | 4/4/2007 9:42:58 PM

Didn't realise @Transform wasn't allowed earlier...

I thought @Explode might work in some way, but couldn't trim it down much further than this:

@text((@TextToTime(@Explode(@texttotime("[2/1-"+@Text(@Adjust([1/1];0;0;@Count(items);0;0;0))+"]")))-[1/1])/86400)+". "+items

30) @17 - Fix
John Smart | 4/4/2007 10:24:15 PM


i:=0;@Eval(@Repeat({(@Text(i:=i+1)+": "+ItemsBiB]):};@Elements(Items))+{""})

Replace "BiB" with the letter "i" within square brackets.

I can't figure out how to get it below 75 chars, though.

31) [/i]@30 Geek Formula Puzzle
Slawek Rogulski | 4/4/2007 11:49:47 PM

i:=0;@Eval(@Repeat({(@Text(i:=i+1)+": "+ItemsBiB]):};@Count(Items))+{""})

32) Geek Formula Puzzle
Nathan T. Freeman | 4/5/2007 5:22:55 AM

My first question is: why are we looking for the least number of characters rather than the fastest execution time? All the examples here use straight list concatenation for the digits instead of @Explode("1,2,3,4,5,6,7,8,9"), which can be considerably faster on frequent iterative operations (such as view column calcs.)

Slawek, you need to test, my friend. Your latest generates an error, and even when you fix it, it generates all possible permutations of the list.

Personally, I think we should amend the original contest to simply say "must be R5 compatible." That eliminates @Eval.

Here's 78 characters...


@subset(@text((a*100*+a*10*+a)+1)+". "+items; @count(items))

33) Geek Formula Puzzle
Nathan T. Freeman | 4/5/2007 5:32:49 AM

@31 - My bad. It's the parser on the comment that's throwing off the input in this comment text.

However, both yours and John Smart's solutions need an @Trim to work properly, because you're generating an extra null item on your list. You can shave a character by replacing the two double-quotes at the end with an unassigned variable.

Substitute the # signs below for open [ and close ] brackets:

i:=0;@Trim(@Eval(@Repeat({(@Text(i:=i+1)+". "+Items#i#):}; @Elements(Items))+{n}))

But it still weighs in at 81 characters like that.

34) Geek Formula Puzzle
Nathan T. Freeman | 4/5/2007 5:59:38 AM

If you shift your thinking away from strictly base 10, you can get some interesting permutation techniques. Because of the original parameter of supporting up to 500 items, we can't get an easy character shave based on the squares of other bases. But if you limit items to, say, 400 elements, then you can use the following to generate your number elements...



That eliminates the extra a*100*+ so you net out to a saved character.

I can't seem to come up with a base sequence yet that cuts more CHARACTERS than that.

35) Geek Formula Puzzle
Kerr Rainey | 4/5/2007 6:30:22 AM

@nathan, "My first question is: why are we looking for the least number of characters rather than the fastest execution time?"

Because it's a puzzle, not a practicle exercise?

Next question;

While this has been fun, the main thrust of the article seemed to be that there is a lot you can do with pre v6 formula that although more esoteric than using a loop, are "better". In the specific example we've ben using, what is the "best" code to use in a real world production environment using v6+ formula? Definitions of "better" and "best" are left as an exercise for the reader.

36) Geek Formula Puzzle
Nathan T. Freeman | 4/5/2007 7:01:29 AM

Here's a different base that shaves a bit more.


@Subset(@Text(j*25*+j+1)+". "+items; @Count(items))

74 characters.

Too bad @formulas don't support the exponention ^ operator.

And 76 characters, but it'll mess with your mind...


@Subset(@Text((j*9*+j)*9*+j+1)+". "+items; @Count(items));

37) Geek Formula Puzzle
Slawek Rogulski | 4/5/2007 8:21:46 AM

@32,33 I was trying to make it shorter by substituting @Count for @Elements in @30 John Smart's solution. And yes the comments formatter adds a bit of its own flavour.

I agree that performance is a consideration in the real world. Also, memory usage is another. Hence I prefer to generate a number list only as long as the Items list. This means that @exploding a date range is the only way to achieve that while being R5 compatible.

My use of @Eval and @Member, etc is a very long way to generate a list of consecutive numbers. John's solution is much more elegant. After reading the help on @Repeat I thought that it would not work since it states there that the resulting string cannot exceed 1024 characters. Alas, it does work with Items list containing up to and over 500 elements.

Just a general question (or should it be posted to Bob Balaban's blog?) How about the ability to do recursion? After all we are playing in a functional world here.

And mine and John's works without trimming. I have tweaked mine and got it down to 132 characters, thats with Nathan's suggestion of replacing the null with an unassigned variable.

u:=@Eval(@Repeat({@Unique:};@Count(i:=Items)-1)+{""});@Explode(@Eval(@Implode({@Text(}+{@Member("}+u+{";u)}+{)};{+"."+}));{.})+{. }+i;

And another variation but its just the same length.

u:=@Eval(@Repeat({@Unique:};@Count(i:=Items)-1)+{z});@Explode(@Eval(@Implode({@Text(n:=@Member("}+u+{";u))+". "+i[n]};{+"!"+}));{!});

38) Geek Formula Puzzle
Richard Hogan | 4/5/2007 9:15:33 AM

Nathan, R5 compatible eliminates @count as well (need to use @elements = + 3 chars!! :-)

39) Geek Formula Puzzle
Betsy Thiede - Notes Geek Groupie | 4/5/2007 9:37:02 AM

Items = "cow":"horse":"pig";

numbers := "0" : "1" : "2" : "3" : "4" : "5" : "6" : "7" : "8" : "9";

num99 := @Subset(@Text(@TextToNumber(numbers *+numbers));-99);

@Subset(num99;@Elements(items)) + ". " + items

40) Geek Formula Puzzle
Betsy Thiede - Notes Geek Groupie | 4/5/2007 9:41:11 AM

Left off the := in Items...

Items := "cow":"horse":"pig";

numbers := "0" : "1" : "2" : "3" : "4" : "5" : "6" : "7" : "8" : "9";

num99 := @Subset(@Text(@TextToNumber(numbers *+numbers));-99);

Subset(num99;@Elements(items)) + ". " + items

41) Geek Formula Puzzle
Slawek Rogulski | 4/5/2007 9:11:46 PM

Thanks Andre for the challenge. It was a nice distraction (and lesson). I hope there will be more. Happy Easter!

42) My answer
Amrit Pal Singh | 4/15/2007 1:37:16 AM

Here is the formula that I can think of ---

x:= "1":"2":"3":"4":"5":"6":"7":"8":"9";

MainList + @Subset((x: (x *+ ("0": x)) : ((x *+ ("0": x)) *+ ("0": x))) ; @Elements(MainList))

Here MainList is the list for which addition is desired.

Also, this works for upto 999 elements max.

Just to illustrate how this works, here is more elaborated version of this-

x:= "1":"2":"3":"4":"5":"6":"7":"8":"9";

y:= "0" : "1":"2":"3":"4":"5":"6":"7":"8":"9";

Nums10To99:= x*+y ;

Nums101To999 := Nums10To99 *+y;

Nums1To999:= x: Nums10To99 : Nums101To999 ;

MainList + @Subset(Nums1To999; @Elements(MainList))

Rest assured, this piece was code by me only.

43) Corrected one
Amrit Pal Singh | 4/15/2007 2:39:28 AM

A small mistake in the previous post. I didnt take into account that the output needs to be in this format -"1. cat".

So here is the final answer-


@Subset((x:(x*+("0":x)):((x*+("0":x))*+("0":x)));@Elements(Items))+". "+Items

44) Geek Formula Puzzle
Aldrin Rasdas | 5/15/2008 3:39:42 AM

nums := "1":"2":"3":"4";

text := "Cat":"Dog":"Cow":"Horse";

nums + "." + text

45) Geek Formula Puzzle
Kerr | 5/15/2008 4:28:39 AM

@44, ZOMG!! U win teh interwebz!!

Though I can hardly talk, it's not like I read the instructions properly either. ;)

 Add a Comment
Comment:  (No HTML - Links will be converted if prefixed http://)
Remember Me?     Cancel

Search this blog 


    About IBM Privacy Contact