在 SAS 数据步骤中编写 Ackermann 函数

Writing the Ackermann function in a SAS data step

为了理解 SAS 中的递归编程,我曾多次尝试编写 two-argument 阿克曼函数的一个版本,但均未成功。

函数指出:

我只打算计算 0 - 3 范围内的值的 m 和 n,因为 m >= 4 的值会导致返回值非常迅速地变大。

我正在拍摄一个相对简单的输出;类似于:

Ack(0,0) = 1
Ack(0,1) = 2
Ack(0,2) = 3
Ack(0,3) = 4
Ack(1,0) = 2
Ack(1,1) = 3

依此类推 Ack(3,3) = 61

我无法在网上找到任何关于有人在 SAS 中执行此操作的参考资料。所以,如果有人能帮我解决这个问题,我将不胜感激!

谢谢!

在普通的 SAS 代码中很难进行递归。但是用宏代码就很容易了。

%macro a(m,n);
%if %sysfunc(verify(&m.&n,0123456789)) %then %do;
  %put WARNING: Invalid input to macro &sysmacroname.. Use only non-negative integers.;
  .
%end;
%else %if (&m=0) %then %eval(&n+1);
%else %if (&n=0) %then %a(%eval(&m-1),1);
%else %a(%eval(&m-1),%a(&m,%eval(&n-1)));
%mend a;

如果您必须将它与数据集变量的值一起使用,那么您可以考虑使用 resolve() 函数。

data testa;
  do i=0 to 3; do j=0 to 3;
    a=input(resolve(cats('%a(',i,',',j,')')),32.);
    output;
  end;end;
run;
proc print; run;

结果

Obs    i    j     a

  1    0    0     1
  2    0    1     2
  3    0    2     3
  4    0    3     4
  5    1    0     2
  6    1    1     3
  7    1    2     4
  8    1    3     5
  9    2    0     3
 10    2    1     5
 11    2    2     7
 12    2    3     9
 13    3    0     5
 14    3    1    13
 15    3    2    29
 16    3    3    61

当然,如果您只打算使用从 0 到 3 的参数,那么也许您只需要使用数组查找。

data testb;
  array _a(0:3,0:3) _temporary_
(1 2 3 4
 2 3 4 5
 3 5 7 9
 5 13 29 61
);
 do i=0 to 3; do j=0 to 3; a=_a(i,j); output; end; end;
run;

这是一个 SAS/AF class 实现

sasuser.examples.ackermanclass.scl

Class Ackerman extends sashelp.fsp.object.class;

  compute: public method
    m: num
    n: num
    return = num;

    if m=0 then return n+1;

    if m > 0 then do;
      if n = 0 then return compute ( m-1, 1 );
      if n > 0 then return compute ( m-1, compute ( m, n-1 ) );

      throw _new_ SASHelp.Classes.SCLException ("Ackerman compute, invalid args: n=" || cats(n));
    end;

    throw _new_ SASHelp.Classes.SCLException ("Ackerman compute, invalid args: m=" || cats(m));
  endmethod;

EndClass;

sasuser.examples.ackermantest.scl

init:

  declare sasuser.examples.ackerman.class ackerman
  = _new_ sasuser.examples.ackerman.class();

  do m = 0 to 3;
  do n = 0 to 3;
    put m= n= 'result=' ackerman.compute(m,n);
  end;
  end;

return;

测试 AFA C=sasuser.examples.ackermantest.scl

m=0 n=0 result=1
m=0 n=1 result=2
m=0 n=2 result=3
m=0 n=3 result=4
m=1 n=0 result=2
m=1 n=1 result=3
m=1 n=2 result=4
m=1 n=3 result=5
m=2 n=0 result=3
m=2 n=1 result=5
m=2 n=2 result=7
m=2 n=3 result=9
m=3 n=0 result=5
m=3 n=1 result=13
m=3 n=2 result=29
m=3 n=3 result=61

这是使用递归的 Proc DS2 示例:

proc ds2;
  data _null_;
    method ackerman(int m, int n) returns int;
      if m=0 then return n+1;

      if m > 0 then do;
        if n = 0 then return ackerman ( m-1, 1 );
        if n > 0 then return ackerman ( m-1, ackerman ( m, n-1 ) );

        return -1;
      end;

      return -1;
    end;

    method init();
      declare int m n result;
      do m = 0 to 3;
      do n = 0 to 3;
        result = ackerman(m,n);
        put m= n= result=;
      end;
      end;
    end;
  enddata;
  run;

quit;

proc fcmp 实施:

/* Define */
proc fcmp outlib=work.funcs.math;
function ackerman(m, n);
  if m = 0 then return(n + 1);
  else if n = 0 then return(ackerman(m - 1, 1));
  else return(ackerman(m - 1, ackerman(m, n - 1)));
endsub;
run;
quit;

/*Test*/
option cmplib = work.funcs;
proc fcmp;
  out = ackerman(3,2);
  put "Testing Function Call";
  put "ackerman(3,2) returns:" out;
quit;