Man or boy test: Difference between revisions

From Rosetta Code
Content added Content deleted
m (Took out all of those source commands)
Line 5: Line 5:


=={{header|ALGOL 60}} - Knuth's example==
=={{header|ALGOL 60}} - Knuth's example==
<source lang="pascal">
begin
begin
real procedure A (k, x1, x2, x3, x4, x5);
real procedure A (k, x1, x2, x3, x4, x5);
Line 17: Line 16:
end;
end;
outreal (A (10, 1, -1, -1, 1, 0));
outreal (A (10, 1, -1, -1, 1, 0));
end;</source>
end;


This creates a tree of ''B'' call frames that refer to each other and to the containing ''A'' call frames, each of which has its own copy of ''k'' that changes every time the associated ''B'' is called. Trying to work it through on paper is probably fruitless, but the correct answer is &minus;67, despite the fact that in the original paper Knuth postulated it to be &minus;121.
This creates a tree of ''B'' call frames that refer to each other and to the containing ''A'' call frames, each of which has its own copy of ''k'' that changes every time the associated ''B'' is called. Trying to work it through on paper is probably fruitless, but the correct answer is &minus;67, despite the fact that in the original paper Knuth postulated it to be &minus;121.
Line 42: Line 41:
This is what happens in the following C implementation:
This is what happens in the following C implementation:


<source lang="C">
/* man-or-boy.c */
/* man-or-boy.c */
#include <stdio.h>
#include <stdio.h>
Line 78: Line 76:
int k = argc == 2 ? strtol(argv[1],0,0) : 10;
int k = argc == 2 ? strtol(argv[1],0,0) : 10;
printf("%d\n", A( FUN(ARG(f1),ARG(f_1),ARG(f_1),ARG(f1),ARG(f0)) ));
printf("%d\n", A( FUN(ARG(f1),ARG(f_1),ARG(f_1),ARG(f1),ARG(f0)) ));
}
}</source>




Line 108: Line 106:
Since Lisp does not have a full range of [[monads in functional programming|monads]] as in Haskell, the Lisp implementation uses ''setq''; a purely functional implementation would be much more complicated:
Since Lisp does not have a full range of [[monads in functional programming|monads]] as in Haskell, the Lisp implementation uses ''setq''; a purely functional implementation would be much more complicated:


<source lang="lisp">
(defun manOrBoy (x)
(defun manOrBoy (x)
(manOrBoy-func x (lambda () 1) (lambda () -1)
(manOrBoy-func x (lambda () 1) (lambda () -1)
Line 124: Line 121:
(if (<= k 0)
(if (<= k 0)
(+ (funcall x4) (funcall x5))
(+ (funcall x4) (funcall x5))
(funcall b))))</source>
(funcall b))))


=={{header|Mathematica}}==
=={{header|Mathematica}}==
Line 130: Line 127:
This ''Mathematica'' code was derived from the Ruby example appearing below.
This ''Mathematica'' code was derived from the Ruby example appearing below.


<source lang="text">
$RecursionLimit = 1665; (* anything less fails for k0 = 10 *)
$RecursionLimit = 1665; (* anything less fails for k0 = 10 *)
Line 139: Line 135:


a[10, 1 &, -1 &, -1 &, 1 &, 0 &] (* => -67 *)
a[10, 1 &, -1 &, -1 &, 1 &, 0 &] (* => -67 *)

</source>
=={{header|PL/I}}==
=={{header|PL/I}}==
morb: proc options (main) reorder;
morb: proc options (main) reorder;
Line 172: Line 168:
Note: the lambda call can be replaced with Proc.new and still work.
Note: the lambda call can be replaced with Proc.new and still work.


<source lang="Ruby">
def a(k, x1, x2, x3, x4, x5)
b = lambda { k -= 1; a(k, b, x1, x2, x3, x4) }
k <= 0 ? x4[] + x5[] : b[]
end


def a(k, x1, x2, x3, x4, x5)
puts a(10, lambda {1}, lambda {-1}, lambda {-1}, lambda {1}, lambda {0})
b = lambda { k -= 1; a(k, b, x1, x2, x3, x4) }
</source>
k <= 0 ? x4[] + x5[] : b[]
end
puts a(10, lambda {1}, lambda {-1}, lambda {-1}, lambda {1}, lambda {0})

=={{header|Smalltalk}} ==
=={{header|Smalltalk}} ==


<source lang="smalltalk">
Number>>x1: x1 x2: x2 x3: x3 x4: x4 x5: x5
Number>>x1: x1 x2: x2 x3: x3 x4: x4 x5: x5
| b k |
| b k |
Line 189: Line 184:
^k <= 0 ifTrue: [ x4 value + x5 value ] ifFalse: b
^k <= 0 ifTrue: [ x4 value + x5 value ] ifFalse: b
10 x1: [1] x2: [-1] x3: [-1] x4: [1] x5: [0]</source>
10 x1: [1] x2: [-1] x3: [-1] x4: [1] x5: [0]


==See also==
==See also==

Revision as of 14:11, 18 December 2007

The man or boy test was proposed by computer scientist Donald Knuth as a means of evaluating implementations of the programming language. The aim of the test was to distinguish compilers that correctly implemented "recursion and non-local references" from those that did not.

 "I have written the following simple routine, which may separate the "man-compilers" from the "boy-compilers" - Donald Knuth" 

ALGOL 60 - Knuth's example

begin
  real procedure A (k, x1, x2, x3, x4, x5);
  value k; integer k;
  begin
    real procedure B;
    begin k:= k - 1;
          B:= A := A (k, B, x1, x2, x3, x4);
    end;
    if k <= 0 then A:= x4 + x5 else B;
  end;
  outreal (A (10, 1, -1, -1, 1, 0));
end;

This creates a tree of B call frames that refer to each other and to the containing A call frames, each of which has its own copy of k that changes every time the associated B is called. Trying to work it through on paper is probably fruitless, but the correct answer is −67, despite the fact that in the original paper Knuth postulated it to be −121.

ALGOL 68

Charles H. Lindsey implemented the algorithm in ALGOL 68, and - as call by name is not necessary - the same algorithm can be implemented in many languages including Pascal and PL/I .

BEGIN
 PROC a = (REAL in k, PROC REAL xl, x2, x3, x4, x5) REAL:
 BEGIN
   REAL k := in k;
   PROC b = REAL:
   BEGIN k := k - 1;
         a(k, b, xl, x2, x3, x4)
   END;
   IF k<=0 THEN x4 + x5 ELSE b FI
 END;
 printf(($+2d.8d$, a(10, REAL:1, REAL:-1, REAL:-1, REAL:1, REAL:0)))
END

C

Even if closures are not available in a language, their effect can be simulated. This is what happens in the following C implementation:

/* man-or-boy.c */
#include <stdio.h>
#include <stdlib.h>

// --- thunks
typedef struct arg {
  int       (*fn)(struct arg*);
  int        *k;
  struct arg *x1, *x2, *x3, *x4, *x5;
} ARG;

// --- lambdas
int f_1 (ARG* _) { return -1; }
int f0  (ARG* _) { return  0; }
int f1  (ARG* _) { return  1; }

// --- helper
int eval(ARG* a) { return a->fn(a); }
#define ARG(...) (&(ARG){ __VA_ARGS__ })
#define FUN(...) ARG(B,&k,__VA_ARGS__)

// --- functions
int B(ARG* a) {
  int A(ARG*);
  int k = *a->k -= 1;
  return A( FUN(a,a->x1,a->x2,a->x3,a->x4) );
}

int A(ARG* a) {
  return *a->k <= 0 ? eval(a->x4)+eval(a->x5) : B(a);
}

int main(int argc, char **argv) {
  int k = argc == 2 ? strtol(argv[1],0,0) : 10;
  printf("%d\n", A( FUN(ARG(f1),ARG(f_1),ARG(f_1),ARG(f1),ARG(f0)) ));
}


Haskell

Haskell is a pure language, so the impure effects of updating k must be wrapped in a state monad.

import Control.Monad.ST
import Data.STRef

type S s = ST s Integer

a :: Integer -> S s -> S s -> S s -> S s -> S s -> S s
a k x1 x2 x3 x4 x5 = a' where
  a' | k <= 0    = do { x4' <- x4; x5' <- x5; return (x4' + x5') }
     | otherwise = do { kr <- newSTRef k; b kr }
  b kr = do
    k <- readSTRef kr
    let k' = k - 1
    writeSTRef kr k'
    a k' (b kr) x1 x2 x3 x4

run k =
  runST (a k (return 1) (return (-1)) (return (-1)) (return 1) (return 0))

Lisp

Since Lisp does not have a full range of monads as in Haskell, the Lisp implementation uses setq; a purely functional implementation would be much more complicated:

(defun manOrBoy (x)
 (manOrBoy-func x (lambda () 1) (lambda () -1)
                  (lambda () -1) (lambda () 1)
                  (lambda () 0)))

(defun manOrBoy-func (k-param x1 x2 x3 x4 x5)
 (let*
   ((k k-param)
    (b
     (lambda ()
       (progn
         (setq k (- k 1))
         (manOrBoy-func k b x1 x2 x3 x4)))))
   (if (<= k 0)
       (+ (funcall x4) (funcall x5))
       (funcall b))))

Mathematica

This Mathematica code was derived from the Ruby example appearing below.

$RecursionLimit = 1665; (* anything less fails for k0 = 10 *)

a[k0_, x1_, x2_, x3_, x4_, x5_] := Module[{k, b },
  k = k0;
  b = (k--; a[k, b, x1, x2, x3, x4]) &;
  If[k <= 0, x4[] + x5[], b[]]]
a[10, 1 &, -1 &, -1 &, 1 &, 0 &] (* => -67 *)

PL/I

morb: proc options (main) reorder;
 dcl sysprint file;

 put skip list(a((10), lambda1, lambda2, lambda3, lambda4, lambda5));

 a: proc(k, x1, x2, x3, x4, x5) returns(fixed bin (31)) recursive;
   dcl k                    fixed bin (31);
   dcl (x1, x2, x3, x4, x5) entry returns(fixed bin (31));

   b: proc returns(fixed bin(31)) recursive;
     k = k - 1;
     return(a((k), b, x1, x2, x3, x4));
   end b;

   if k <= 0 then
     return(x4 + x5);
   else
     return(b);
 end a;

 lambda1: proc returns(fixed bin (31)); return(1);  end lambda1;
 lambda2: proc returns(fixed bin (31)); return(-1); end lambda2;
 lambda3: proc returns(fixed bin (31)); return(-1); end lambda3;
 lambda4: proc returns(fixed bin (31)); return(1);  end lambda4;
 lambda5: proc returns(fixed bin (31)); return(0);  end lambda5;
end morb;

Ruby

Note: the lambda call can be replaced with Proc.new and still work.


def a(k, x1, x2, x3, x4, x5)
  b = lambda { k -= 1; a(k, b, x1, x2, x3, x4) }
  k <= 0 ? x4[] + x5[] : b[]
end

puts a(10, lambda {1}, lambda {-1}, lambda {-1}, lambda {1}, lambda {0})

Smalltalk

Number>>x1: x1 x2: x2 x3: x3 x4: x4 x5: x5
   | b k |
   k := self.
   b := [ k := k - 1. k x1: b x2: x1 x3: x2 x4: x3 x5: x4 ].
   ^k <= 0 ifTrue: [ x4 value + x5 value ] ifFalse: b

10 x1: [1] x2: [-1] x3: [-1] x4: [1] x5: [0]

See also