haskell可以优化尾部递归函数吗?

时间:2017-01-09 16:00:52

标签: haskell recursion optimization

我有以下计划:

my_sum 0 = 54321
my_sum i = i + (my_sum (i - 1))

main = do
    print $ my_sum 12345

我想知道生成的my_sum函数是否是递归的。换句话说,haskell会将此变换为非递归类型。

我试图看到本机c--代码(asm几乎相同)。但这对我来说太难了。代码为my_sum

my_sum_s24N_entry() //  [R1]
     { info_tbl: [(c25x,
                   label: block_c25x_info
                   rep:StackRep [False, False]),
                  (c267,
                   label: my_sum_s24N_info
                   rep:HeapRep 1 nonptrs { Fun {arity: 1 fun_type: ArgSpec 5} })]
       stack_info: arg_space: 8 updfr_space: Just 4
     }
 {offset
   c267:
       _s24N::P32 = R1;
       _s24O::P32 = P32[Sp];
       if ((Sp + 8) - 32 < SpLim) goto c268; else goto c269;
   c269:
       Hp = Hp + 8;
       if (Hp > HpLim) goto c26b; else goto c26a;
   c26b:
       HpAlloc = 8;
       goto c268;
   c268:
       R1 = _s24N::P32;
       call (stg_gc_fun)(R1) args: 8, res: 0, upd: 4;
   c26a:
       I32[Hp - 4] = sat_s24V_info;
       _c25n::P32 = Hp - 4;
       I32[Sp - 8] = c25x;
       P32[Sp - 24] = GHC.Integer.Type.$fEqInteger_closure;
       I32[Sp - 20] = stg_ap_pp_info;
       P32[Sp - 16] = _s24O::P32;
       P32[Sp - 12] = _c25n::P32;
       P32[Sp - 4] = _s24N::P32;
       Sp = Sp - 24;
       call GHC.Classes.==_info() returns to c25x, args: 20, res: 4, upd: 4;
   c25x:
       _s24N::P32 = P32[Sp + 4];
       _s24O::P32 = P32[Sp + 8];
       _s24W::P32 = R1;
       _c266::P32 = _s24W::P32 & 3;
       if (_c266::P32 != 1) goto c265; else goto c264;
   c265:
       Hp = Hp + 8;
       if (Hp > HpLim) goto c26j; else goto c26i;
   c26j:
       HpAlloc = 8;
       R1 = _s24W::P32;
       call stg_gc_unpt_r1(R1) returns to c25x, args: 4, res: 4, upd: 4;
   c26i:
       I32[Hp - 4] = GHC.Integer.Type.S#_con_info;
       I32[Hp] = 54321;
       _c26k::P32 = Hp - 3;
       P32[Sp] = GHC.Num.$fNumInteger_closure;
       I32[Sp + 4] = stg_ap_p_info;
       P32[Sp + 8] = _c26k::P32;
       call GHC.Num.fromInteger_info() args: 16, res: 0, upd: 4;
   c264:
       Hp = Hp + 16;
       if (Hp > HpLim) goto c26e; else goto c26d;
   c26e:
       HpAlloc = 16;
       R1 = _s24W::P32;
       call stg_gc_unpt_r1(R1) returns to c25x, args: 4, res: 4, upd: 4;
   c26d:
       I32[Hp - 12] = sat_s250_info;
       P32[Hp - 4] = _s24N::P32;
       P32[Hp] = _s24O::P32;
       _c25B::P32 = Hp - 12;
       P32[Sp - 4] = GHC.Num.$fNumInteger_closure;
       I32[Sp] = stg_ap_pp_info;
       P32[Sp + 4] = _s24O::P32;
       P32[Sp + 8] = _c25B::P32;
       Sp = Sp - 4;
       call GHC.Num.+_info() args: 20, res: 0, upd: 4;
 }
 } 

1 个答案:

答案 0 :(得分:3)

答案是否定的,GHC不会将此优化为尾递归函数。正如评论者所说,要看到这一点,你应该看看GHC核心^:

Rec {
my_sum :: Integer -> Integer
my_sum =
  \ (ds :: Integer) ->
    case eqInteger# ds my_sum'2 of wild { __DEFAULT ->
    case tagToEnum# wild of _ {
      False -> plusInteger ds (my_sum (minusInteger ds lvl));
      True -> my_sum'1
    }
    }
end Rec }

这个函数显然不是尾递归的 - 最外面的调用是plusInteger。 GHC核心是执行所有优化后的代码 - 此代码实际上是转换为机器代码的代码。所以你知道你的函数在核心中是不是尾递归的,它实际上不是尾递归的。它本质上只是Haskell,除了一些有趣的名字,所以阅读它不应该是一个问题。

如果你想要一个尾递归函数,只需使用foldl'

import Data.List (foldl')

my_sum' :: Integer -> Integer 
my_sum' i0 = foldl' (+) 54321 [0..i0]

其中给出了以下核心:

my_sum' :: Integer -> Integer
my_sum' =
  \ (i0 :: Integer) ->
    letrec {
      go :: Integer -> Integer -> Integer
      go =
        \ (x :: Integer) (eta :: Integer) ->
          case gtInteger# x i0 of wild { __DEFAULT ->
          case tagToEnum# wild of _ {
            False -> go (plusInteger x $fEnumInteger1) (plusInteger eta x);
            True -> eta
          }
          }; } in
    go my_sum'2 my_sum'1

此处my_sum'2my_sum'1lvl是该计划中的常量:

my_sum'2 :: Integer
my_sum'2 = 0
my_sum'1 :: Integer
my_sum'1 = 54321
lvl :: Integer
lvl = 1

^使用选项-ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes --make生成