As 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]) + ")"))); result`

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)

Comments

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 }

**Geek Formula Puzzle**

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;

**Geek Formula Puzzle**

how about:

items:="cow":"horse":"pig";

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

**Geek Formula Puzzle**

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

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

**Geek Formula Puzzle v1.1**

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

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

**Geek Formula Puzzle**

min:=1;

max:=@Elements( Items );

startDate:=[01/01/2001];

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

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

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

**Geek Formula Puzzle**

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 ;)

c:=0;

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

**Geek Formula Puzzle**

Looks likeyou don't need the @do.

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

**Geek Formula Puzzle**

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

**Geek Formula Puzzle**

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

**Geek Formula Puzzle**

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).

**Geek Formula Puzzle**

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

**Geek Formula Puzzle**

uniqueList:=@Explode(@Eval(@Repeat("@Eval(\"@Unique\")"+"+\".\"+";@Elements(Items))+"\"\"");".");

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

**Geek Formula Puzzle**

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

**re: Geek Formula Puzzle**

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.

**Geek Formula Puzzle**

@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. ;-)

**Oooh, Eval!**

I hadn't thought of that Eval and Repeat.

i := 0;

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

74 chars

*18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
*

**@17 - accidental markup**

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

**Geek Formula Puzzle**

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

**re: Oooh, Eval!**

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

**re: Geek Formula Puzzle**

Insufficient arguments for @Function.

**Geek Formula Puzzle**

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.

**Arggh! :-)**

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

**re: Arggh! :-)**

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

**Geek Formula Puzzle**

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.

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

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

**Geek Formula Puzzle**

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

a:=(0:1:2:3:4:5:6:7:8:9);

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

84 characters.

**Geek Formula Puzzle**

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)

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

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

83, including the space in ". "

**Geek Formula Puzzle**

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))

**Geek Formula Puzzle**

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

**@17 - Fix**

Sorry.

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.

**[/i]@30 Geek Formula Puzzle**

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

**Geek Formula Puzzle**

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

a:=0:2:4:6:8*+0:1;

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

**Geek Formula Puzzle**

@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.

**Geek Formula Puzzle**

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

a:=0:4:8:12:16*+0:1:2:3;

a*20*+a

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.

**Geek Formula Puzzle**

@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.

**Geek Formula Puzzle**

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

i:=0:1:2:3:4;j:=i*5*+i;

@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...

i:=0:1:2;j:=i*3*+i;

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

**Geek Formula Puzzle**

@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]};{+"!"+}));{!});

**Geek Formula Puzzle**

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

**Geek Formula Puzzle**

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

**Geek Formula Puzzle**

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

**Geek Formula Puzzle**

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

**My answer**

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.

**Corrected one**

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-

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

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

**Geek Formula Puzzle**

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

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

nums + "." + text

**Geek Formula Puzzle**

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

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

Geek Formula Puzzle