2017-09-22 5 views
7

私は最近、このコードをJavaで見つけました。これは機能とフィボナッチ数の印刷を含み、機能します。Javaの再帰ラムダコールのこの部分はどのように動作しますか

public class AppLambdaSubstitution { 

public static Function<Integer, Integer> Y(Function<Function<Integer, Integer>, Function<Integer, Integer>> f) { 
    return x -> f.apply(Y(f)).apply(x); 
} 

public static void main(String[] args) { 
    Function<Integer, Integer> fib = Y(
      func -> x -> { 
     if (x < 2) 
      return x; 
     else 
      return func.apply(x - 1) + func.apply(x - 2); 
    }); 

    IntStream.range(1,11). 
    mapToObj(Integer::valueOf). 
    map(fib).forEach(System.out::println); 
    } 
} 

私が混乱している部分はreturn x -> f.apply(Y(f)).apply(x);です。 Y(f)はメソッドYへの再帰呼び出しではありませんか?関数fをパラメータとして呼び出しています。私には、この再帰的呼び出しが元に戻ってくるための基本的なケースはありません。無限の再帰呼び出しの結果、オーバーフローが発生しないのはなぜですか?

+3

'Y(f)を'コールはラムダの内側にあります'f'がそれを呼び出すことを選択した場合にのみ、ラムダは実行されます。 – 4castle

+4

https://en.wikipedia.org/wiki/Fixed-point_combinator – pvg

答えて

5

x -> f.apply(Y(f)).apply(x);は、applyと呼ばれない点がありません。returnは、Functionです。

これは、カリングと再帰関数IMOを示す非常に複雑な(直感的ではない)方法です。あなたがいくつかのものを置き換え、それを少しだけ読みやすくするならば、事はもっと簡単になります。

この建設:左のパラメータがないが全くを使用しているので、

Function<Function<Integer, Integer>, Function<Integer, Integer>> 

は、まったく必要ありません。正しいものを手に入れるだけです。このように、leftのパラメータはとなります。となります(後でSupplierと置き換えますが、どちらも必要ではありませんが、ポイントを証明するだけです)。

実際にあなたがここで気にすべてが、これはStreamの各要素のための実際の計算んFunctionです:

Supplier<Function<Integer, Integer>> toUse =() -> right(); 
Function<Integer, Integer> fib = curry(toUse); 
IntStream.range(1, 11) 
      .mapToObj(Integer::valueOf) 
      .map(fib) 
      .forEach(System.out::println); 

このSupplier<Function<Integer, Integer>> toUse =() -> right();

public static Function<Integer, Integer> right() { 

    return new Function<Integer, Integer>() { 
     @Override 
     public Integer apply(Integer x) { 
      if (x < 2) { 
       return x; 
      } else { 
       return apply(x - 1) + apply(x - 2); 
      } 
     } 
    }; 
} 

今、あなたがその全体構造を書くことができ前の例(Function<Function, Function>)のの左部分がである理由を理解してください。ちょうどright 1。

あなたも近い見れば、あなたは、このようにあなたがさらにでそれを簡素化することができ、Supplierが完全をを必要とされていないことに気づくかもしれません:

IntStream.range(1, 11) 
     .mapToObj(Integer::valueOf) 
     .map(right()) 
     .forEach(System.out::println); 
関連する問題