2
\$\begingroup\$

Inspired by Sygmei's question, I'm interested in finding out what languages can "do this properly".

Your task is to write a "for loop" function / macro / instruction. The arguments should be:

  • an initializer
  • a test
  • an increment
  • a body

All of these are "pieces of code" and should be able to access variables in the context of the call. The body should be able to include multiple statements.

The aim is to have the fewest lexical tokens excluding comments, except that #define and friends (using a dumb preprocessor) are worth 50, and eval and friends (taking a string argument) are worth 100. Feel free to make your code readable with comments, whitespace and meaningful names.

You should include a "test harness" which is not counted towards length.

The C version definitely won't win:

// the do {...} while (0) idiom enables a code block to be a single C statement, so it can safely be followed by a semicolon
#define my_for(init, test, inc, body) do {  \
    init; \
    while (test) { \
        body; \
        inc; \
    } \
} while (0)

// test harness:
#include <stdio.h>

main()
{
    int i, total;
    my_for((i=1, total=0), i<=3, ++i, {printf("%d\n", i); total=total+i;});
    printf("total: %d\n", total);
}

Edit to add: the meaning of "lexical token" depends on your language. Here's how the scoring breaks down for my C version:

//[...]: 0
#define: 1 occurrence, 50 points
my_for: 1
(: 3
init: 2
,: 3
test: 2
inc: 2
body: 2
): 3
do: 1
{: 2
\: 6
;: 3
while: 2
}: 2
0: 1

TOTAL: 85

\$\endgroup\$
13
  • 3
    \$\begingroup\$ Unclear to me: scoring. For example how would your c reference be scored? \$\endgroup\$ Jan 27, 2017 at 4:42
  • \$\begingroup\$ I was actually going to do this challenge, but I ran into a fair number of difficulties in laying it out. I hope someone posts a well explained version; preferably normal code golf. \$\endgroup\$ Jan 27, 2017 at 16:18
  • \$\begingroup\$ @DigitalTrauma does my edit clarify it sufficiently? If so how can I get the question re-opened? I can't tag all the on-hold-voters in this comment \$\endgroup\$
    – Hugh Allen
    Jan 28, 2017 at 2:40
  • \$\begingroup\$ The C scoring only makes it clear for C programs. It's very hard to define "tokens" for a wide range of languages. You either have to give up on the idea, or only allow a narrow range of languages (which is a whole different can of worms). \$\endgroup\$
    – user45941
    Jan 28, 2017 at 4:29
  • \$\begingroup\$ 1) Can it be a lambda (anonymous function), or does it have to be a named function? 2) Is it okay to use the language's built-in for loop syntax in the implementation? \$\endgroup\$
    – smls
    Jan 28, 2017 at 22:28

3 Answers 3

2
\$\begingroup\$

Python, 11 26 tokens:

def f(n, t, i, l):
    n()
    while t():
        l()
        i()

This is my first time doing , so forgive me if I got the token counting wrong.

EDIT: Hugh Allen informed me that I got the token counting wrong.

\$\endgroup\$
7
  • \$\begingroup\$ The thing is, atomic-code-golf doesn't actually exist. \$\endgroup\$
    – feersum
    Jan 27, 2017 at 6:17
  • 5
    \$\begingroup\$ @feersum It has 44 questions. How does it not exist? \$\endgroup\$
    – Wheat Wizard
    Jan 27, 2017 at 19:36
  • \$\begingroup\$ That's a nice answer, but it's more than 11 tokens! See my edit for clarification (I hope) \$\endgroup\$
    – Hugh Allen
    Jan 28, 2017 at 2:41
  • \$\begingroup\$ I can't see your edit. Could you explain to me why it's more than 11 tokens? \$\endgroup\$ Jan 28, 2017 at 6:05
  • 1
    \$\begingroup\$ @HughAllen So 26 tokens? \$\endgroup\$ Jan 28, 2017 at 19:27
1
\$\begingroup\$

Perl 6, 24 tokens

sub my-for (&init, &test, &inc, &body) {
    init;
    while (test) { body; inc }
}

Tokens:

while   1   keyword
sub     1   keyword
my-for  1   function name (declaration)
&init   1   function parameter (declaration)
&test   1   function parameter (declaration)
&inc    1   function parameter (declaration)
&body   1   function parameter (declaration)
init    1   function name (implicit call)
test    1   function name (implicit call)
inc     1   function name (implicit call)
body    1   function name (implicit call)
,       3   parameter list separator
;       2   statement separator
{       2   block opener
}       2   block closer
(       2   parameter list opener
)       2   parameter list closer

Test:

my ($i, $total);
my-for {$i=1; $total=0}, {$i <= 3}, {++$i}, {
    say $i;
    $total = $total + $i;
}
say "total: $total";
\$\endgroup\$
0
\$\begingroup\$

tcl, could I say 0 due to point 1.?

If the question was "how many ways your language allow you to do it", tcl would certainly score a good amount.

  1. for

The language's normal for works this way; it should me noted that tcl has no keywords, so for works exactly like a function (side note: its behaviour can even be redefined!). Then, there is no need to implement it:

for {set i 0;set total 1} {$i<=3} {incr i} {puts $i; incr total $i}
puts total:\ $total
  1. proc

Implement a function, strictly according to the question.

2.1. uplevel

The uplevel 1 will execute its contents 1 level up in the call stack, to allow there the access to the variables of the for input; otherwise we could not access $total, because it would be not defined.

proc F {s t i b} {uplevel 1 [list for $s $t $i $b]}
F {set i 0;set total 1} {$i<=3} {incr i} {puts $i; incr total $i}
puts total:\ $total

2.2. tailcall

From tailcall documentation: "The tailcall command replaces the currently executing procedure, lambda application, or method with another command."

proc P args {tailcall for {*}$args}
P {set i 0;set total 1} {$i<=3} {incr i} {puts $i; incr total $i}
puts total:\ $total
  1. interp alias

You can say to one of the interpreters currently existing on interpreters tree that a command has an alias. I didn't create any interpreter, so I am interested on the tree's root interpreter, which path is an empty string, represented by {}. The following line means: "Create the alias R on the root interpreter, which will re-route calls to it to for on the root interpreter.

interp alias {} R {} for
R {set i 0;set total 1} {$i<=3} {incr i} {puts $i; incr total $i}
puts total:\ $total
  1. set

You can define a var that will have the same value has for command:

set S for
$S {set i 0;set total 1} {$i<=3} {incr i} {puts $i; incr total $i}
puts total:\ $total
  1. rename

You can change the name of the function. Note: From now on, you can not call for directly again! Now you must always call f, until you rename it back!

rename for f
f {set i 0;set total 1} {$i<=3} {incr i} {puts $i; incr total $i}
puts total:\ $total

demo

\$\endgroup\$
1
  • \$\begingroup\$ 1. Almost every language has a "for" loop; giving the builtin one is definitely cheating. 2. uplevel is like eval and is worth 100 points! \$\endgroup\$
    – Hugh Allen
    Jan 29, 2017 at 1:59

Not the answer you're looking for? Browse other questions tagged or ask your own question.