代码之家  ›  专栏  ›  技术社区  ›  user6445533

为什么通过reduce实现的函数组合返回闭包?

  •  8
  • user6445533  · 技术社区  · 8 年前

    我想从reduce/fold推导出n-函数的合成函数,但它没有按预期工作:

    $id = function ($x) {
      return $x;
    };
    
    $comp = function ($f) {
      return function ($g) use ($f) {
        return function ($x) use ($f, $g) {
          return $f($g($x));
        };
      };
    };
    
    $fold = function ($f, $acc) {
      return function ($xs) use ($f, &$acc) {
        return array_reduce($xs, $f, $acc);
      };
    };
    
    $compn = function($fs) {/* apply $fold here */};
    
    $inc = function ($n) {
      return $n + 1;
    };
    
    $fold($comp, $id) ([$inc, $inc, $inc]) (0); // yields a closure instead of 3
    

    我在Javascript中实现了相同的函数,它可以工作。我使用PHP 7.0.8 cli。我对PHP了解不多,所以我可能忽略了一些东西。

    2 回复  |  直到 8 年前
        1
  •  2
  •   user6445533 user6445533    8 年前

    我想出来了: array_reduce 电话 $f 作为多参数函数。所以我必须引入另一个匿名函数:

    $id = function ($x) {
      return $x;
    };
    
    $comp = function ($f) {
      return function ($g) use ($f) {
        return function ($x) use ($f, $g) {
          return $f($g($x));
        };
      };
    };
    
    $fold = function ($f, $acc) {
      return function ($xs) use ($f, &$acc) {
        return array_reduce($xs, function ($acc_, $x) use ($f) {
    //                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          return $f($acc_) ($x);
        }, $acc);
      };
    };
    
    $compn = function($fs) {/* apply $fold here */};
    
    $inc = function ($n) {
      return $n + 1;
    };
    
    echo $fold($comp, $id) ([$inc, $inc, $inc]) (0); // yields 3
    

    下面是简化/折叠包装器,以获得更好的API:

    $compn = function (...$fs) use ($fold, $comp, $id) {
      return $fold($comp, $id) ($fs);
    };
    
    $compn($inc, $inc, $inc) (0);
    
        2
  •  1
  •   Mulan    8 年前

    你的 $comp curried ,当然,您发现了PHP的本机 array_reduce 期望函数接受多个参数,快速应用 uncurry 一些 但是如果你想知道如何从整体上改善这一点,你需要继续阅读。。。


    在PHP看来。。。

    使用 未结婚 这样做是有道理的,但如果所有函数都定义为 $ -命名变量——我预见到使用这种风格会出现很多小问题。

    PHP有一个 callable “type”使事情变得更像PHP-ish–user-defined函数(包括高阶函数)应该使用 call_user_func call_user_func_array

    namespace my\module;
    
    function identity ($x) {
      return $x;
    }
    
    function comp ($f) {
      return function ($g) use ($f) {
        return function ($x) use ($f, $g) {
          return call_user_func ($f, call_user_func ($g, $x));
        };
      };
    }
    
    function uncurry ($f) {
      return function ($x, $y) use ($f) {
        return call_user_func (call_user_func ($f, $x), $y);
      };
    }
    
    function fold ($f, $acc) {
      return function ($xs) use ($f, $acc) {
        return array_reduce ($xs, uncurry ($f), $acc);
      };
    }
    

    现在你的 compn 带可变接口,工作正常

    function compn (...$fs) {
      return fold ('comp', 'identity') ($fs);
    }
    
    function inc ($x) {
      return $x + 1;
    }
    
    echo compn ('inc', 'inc', 'inc') (0); // 3
    

    但它也适用于匿名函数

    $double = function ($x) { return $x + $x; };
    
    echo compn ($double, $double, 'inc') (2); // 12
    

    功能代码,模块化程序

    使用声明函数 function 语法,您可以 import 将其扩展到您计划的其他领域

    // single import
    use function my\module\fold;
    
    // aliased import
    use function my\module\fold as myfold;
    
    // multiple imports
    use function my\module\{identity, comp, compn, fold};
    

    现在你不必浪费你的代码 use -每次想使用函数时都会阻塞

    // before
    $compn = function (...$fs) use ($fold, $comp, $id) {
      return $fold($comp, $id) ($fs);
    };
    
    // after
    function compn (...$fs) {
      return fold ('comp', 'id') ($fs);
    }
    

    说到调试,毫无疑问 已命名 函数还将提供更有用的堆栈跟踪消息


    相关但不重要

    PHP添加 可赎回 类型,但我确信这与您无关,因为它们与OOP相关–例如,

    类方法调用

    // MyClass::myFunc (1);
    call_user_func (['MyClass', 'myFunc'], 1);
    

    对象方法调用

    // $me->myfunc (1);
    call_user_func ([$me, 'myfunc'], 1);