zoukankan      html  css  js  c++  java
  • PHP的匿名函数和闭包

    匿名函数

    // Example1
    $func = function( $param ) {
        echo $param;
    };
    $func( 'some string' );//输出:some string
    
    // Example2
    function callFunc( $func ) {
        $func( 'some string' );
    }
    $printStrFunc = function( $str ) {
        echo $str;
    };
    callFunc( $printStrFunc );
    
    //可以直接将匿名函数进行传递。如果你了解js,这种写法可能会很熟悉
    callFunc( function( $str ) {
        echo $str;
    } );
    

    闭包

    PHP在默认情况下,匿名函数内不能调用所在代码块的上下文变量,而需要通过使用use关键字。

    //1. 通过闭包获取当前函数环境外的变量值副本。
    function getMoney() {
        $rmb = 1;
        $dollar = 6;
        $func = function() use ( $rmb ) {
            echo $rmb; //1
            echo $dollar; //报错,找不到dorllar变量
        };
        $func();
    }
    getMoney();
    
    //2. 之所以称为副本,是因为通过闭包传值到匿名函数内的变量,值也是不能改变。
    function getMoney() {
        $rmb = 1;
        $func = function() use ( $rmb ) {
            $rmb += 2;
            echo $rmb; // 3
        };
        $func();
        echo $rmb; // 还是1没有改变;
    }
    getMoney();
    
    //3. 如果要改变外部变量的值,还是得通过传址的方法
    function getMoney() {
        $rmb = 1;
        $func = function() use ( &$rmb ) {
            $rmb += 2;
            echo $rmb; // 3
        };
        $func();
        echo $rmb; // 3;
    }
    getMoney();
    
    //4. 
    function getMoneyFunc() {
        $rmb = 1;
        $func = function() use(&$rmb){
            echo $rmb;
            //把$rmb的值加1
            $rmb++;
        };
        return $func; // 如果将匿名函数返回给外界,匿名函数会保存use所引用的变量,而外界则不能得到这些变量,这样形成‘闭包’
    }
    
    $getMoney = getMoneyFunc();
    $getMoney(); // 1
    $getMoney(); // 2
    $getMoney(); // 3
    

    闭包的好处

    1. 减少循环

    // 一个基本的购物车,包括一些已经添加的商品和每种商品的数量。
    // 其中有一个方法用来计算购物车中所有商品的总价格。该方法使用了一个closure作为回调函数。
    class Cart{
        const PRICE_BUTTER = 1.00;
        const PRICE_MILK   = 3.00;
        const PRICE_EGGS   = 6.95;
    
        protected $products = array();
    
        public function add($product , $quantity)
        {
            $this->products[$product] = $quantity;
        }
    
        public function getQuantity($product)
        {
            return isset($this->products[$product]) ? $this->products[$product] : false;
        }
    
        public function getTotal($tax)
        {
            $total = 0.00;
            // 使用闭包减少循环;
            $callback = function($quantity , $product) use ($tax , &$total){
                $pricePerItem = constant(__CLASS__ . "::PRICE_" . strtoupper($product));
                $total += ($pricePerItem * $quantity) * ($tax + 1.0);
            };
            array_walk($this->products , $callback);
            return round($total , 2);;
        }
    }
    
    $my_cart = new Cart;
    // 往购物车里添加条目
    $my_cart->add('butter' , 1);
    $my_cart->add('milk' , 3);
    $my_cart->add('eggs' , 6);
    // 打出出总价格,其中有 5% 的销售税.
    print $my_cart->getTotal(0.05) . "
    ";
    // The result is 54.29
    

    2. 减少函数的参数

    function html($code , $id = "" , $class = "")
    {
        if ($id !== "")
            $id = " id = "{$id}"";
        $class = ($class !== "") ? " class ="$class"" : "";
        $open = "<$code$id$class>";
        $close = "</$code>";
        return function($inner = "") use ($open , $close){
            return "$open$inner$close";
        };
    }
    $tag = html('div','','class');
    
    // 可读性和可维护性大大提高;
    echo $tag('div1,div1,div1');
    echo PHP_EOL;
    echo $tag('div2,div2,div2');
    
    

    3. 解除递归函数

    // ↓↓ 注意,这里的fib一定要用引用哦,因为第一次的时候就会Notice: Undefined variable,然后后面的fib()就会错误;
    $fib = function($n) use (&$fib){
        if ($n == 0 || $n == 1)
            return 1;
        return $fib($n - 1) + $fib($n - 2);
    };
    echo $fib(2) . "
    "; // 2
    $lie = $fib;
    $fib = function(){
        die('error');
    };//rewrite $fib variable
    
    echo $lie(5); // error  达到递归解除;
    

    4. 关于延迟绑定

    $result = 0;
    $one = function(){
        var_dump($result);
    };
    $two = function() use ($result){
        var_dump($result);
    };
    
    // 如果使用引用,就能使use里面的变量完成延迟绑定,也就是在调用的时候再赋值;
    $three = function() use (&$result){
        var_dump($result);
    };
    
    $four = function() use ($result){
        var_dump($result); //在回调生成的时候进行赋值;
    };
    $result += 1;
    
    $one();    // outputs NULL: $result is not in scope
    $two();    // outputs int(0): $result was copied
    $three();    // outputs int(1)
    $four(); // outputs int(1)
    

    几个配合回调或闭包的函数

    bool array_walk ( array &$array , callable $funcname [, mixed $userdata = NULL ] )

    /**
     * @param array $array
     * @param callable $funcname ()
     * @param mixed|NULL $userdata
     * @return bool
     * bool array_walk ( array &$array , callable $funcname [, mixed $userdata = NULL ] )
     */
    $fruits = array(
        "d" => "lemon" ,
        "a" => "orange" ,
        "b" => "banana" ,
        "c" => "apple"
    );
    $test_print = function(&$item2 , $key, $prefix){
        $item2 .= ' 10';
        echo "{$prefix} : $key => $item2
    ";
    };
    /*
        this result : d => lemon
        this result : a => orange
        this result : b => banana
        this result : c => apple
     */
    array_walk($fruits , $test_print, 'this result');
    
    print_r($fruits);
    /*
        Array
        (
            [d] => lemon 10
            [a] => orange 10
            [b] => banana 10
            [c] => apple 10
        )
     */
    

    bool array_walk_recursive ( array &$input , callable $funcname [, mixed $userdata = NULL ]

    $sweet = array(
        'a' => 'apple' ,
        'b' => 'banana'
    );
    $fruits = array(
        'sweet' => $sweet ,
        'sour' => 'lemon'
    );
    $test_print = function($item , $key)
    {
        echo "$key holds $item
    ";
    };
    
    array_walk_recursive($fruits , $test_print);
    /*
     * 自动跳过sweet,因为sweet是数组;任何其值为 array 的键都不会被传递到回调函数中去
        a holds apple
        b holds banana
        sour holds lemon
     */
    

    array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

    $odd = function($var){
        return ($var & 1);
    };
    $even = function($var){
        return (!($var & 1));
    };
    $array1 = array( "a" => 1 , "b" => 2 , "c" => 3 , "d" => 4 , "e" => 5 );
    $array2 = array( 6 , 7 , 8 , 9 , 10 , 11 , 12 );
    echo "Odd :
    ";
    print_r(array_filter($array1 , "odd"));
    /*
    Odd :
    Array
    (
        [a] => 1
        [c] => 3
        [e] => 5
    )
     */
    echo "Even:
    ";
    print_r(array_filter($array2 , "even"));
    /*
    Even:
    Array
    (
        [0] => 6
        [2] => 8
        [4] => 10
        [6] => 12
    )
     */
     
     # 如果不传第二参数的的话
     $entry = array(
        0 => 'foo',
        1 => false,
        2 => -1,
        3 => null,
        4 => ''
    );
    print_r(array_filter($entry));
    /*
     * 当前值为false的话就filter;
    Array
    (
        [0] => foo
        [2] => -1
    )
     */
    

    array array_map ( callable $callback , array $arr1 [, array $array ] )

    /**
     * @param callable $callback
     * @param array $arr1
     * @param array $array
     */
    
    $func = function($value) {
        return $value * 2;
    };
    
    print_r(array_map($func, range(1, 5)));
    
    /*
    Array
    (
        [0] => 2
        [1] => 4
        [2] => 6
        [3] => 8
        [4] => 10
    )
     */
     
     $show_Spanish = function($n , $m){
        return ("The number $n is called $m in Spanish");
    };
    $a = array( 1 , 2 , 3 , 4 , 5 );
    $b = array( "uno" , "dos" , "tres" , "cuatro" , "cinco" );
    $c = array_map($show_Spanish , $a , $b);
    /**
    print_r($c);
    Array
    (
    [0] => The number 1 is called uno in Spanish
    [1] => The number 2 is called dos in Spanish
    [2] => The number 3 is called tres in Spanish
    [3] => The number 4 is called cuatro in Spanish
    [4] => The number 5 is called cinco in Spanish
    )
     */
    
    $map_Spanish = function($n , $m){
        return (array($n => $m));
    };
    $d = array_map($map_Spanish , $a , $b);
    print_r($d);
    /**
        Array (
        [0] => Array ( [1] => uno )
        [1] => Array ( [2] => dos )
        [2] => Array ( [3] => tres )
        [3] => Array ( [4] => cuatro )
        [4] => Array ( [5] => cinco )
        )
     */
    

    mixed array_reduce ( array $input , callable $function [, mixed $initial = NULL ] )

    
    /**
     * 用回调函数迭代地将数组简化为单一的结果值,解释不清楚的一看代码就明白了;
     * @param array $input
     * @param callable $function
     * @param mixed|NULL $initial 如果指定了可选参数 initial,该参数将被当成是数组中的第一个值来处理,或者如果数组为空的话就作为最终返回值。
     */
    
    $rsum = function($result , $value){
        // $result 初始值为NULL, 如果有第三参数的话,第三参数为初始值;
        $result += $value;
        return $result;
    };
    
    $rmul = function($result , $value){
        $result *= $value;
        return $result;
    };
    
    $a = array(1, 2, 3, 4, 5);
    $x = array();
    $b = array_reduce($a, $rsum); // (NULL)0+1+2+3+4+5 = 15;
    $c = array_reduce($a, $rmul, 10); // 10*1*2*3*4*5 = 1200;
    $d = array_reduce($x, $rsum, "No data to reduce"); // No data to reduce
    

    mixed preg_replace_callback ( mixed $pattern , callable $callback , mixed $subject [, int $limit = -1 [, int &$count ]] )

    /**
     * @param mixed $pattern 正则模式;
     * @param callable $callback
     * @param mixed $subject
     * @param int $limit 对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
     * @param int $count 如果指定,这个变量将被填充为替换执行的次数。
     * mixed preg_replace_callback ( mixed $pattern , callable $callback , mixed $subject [, int $limit = -1 [, int &$count ]] )
     */
    // 将文本中的年份增加一年.
    $text = "April fools day is 04/01/2002
    ";
    $text .= "Last christmas was 12/24/2001
    ";
    
    // 回调函数
    $next_year = function($matches){
        // 通常: $matches[0]是完成的匹配
        // $matches[1]是第一个捕获子组的匹配
        // 以此类推
        return $matches[1] . ($matches[2] + 1);
    };
    
    echo preg_replace_callback("|(d{2}/d{2}/)(d{4})|" , $next_year , $text);
    

    mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )

    mixed call_user_func_array ( callable $callback , array $param_arr )

    /**
     * @param callable $callback 第一个参数为需要调用的函数; 如果是数组array($classname, $methodname)
     * @param mixed $parameter 第二个参数开始就是队列进该函数的参数;
     * @param mixed $parameter2
     * @param mixed $parameter3
     * ..
     * @return 返回值:返回调用函数的结果,或FALSE
     */
    $eat = function($fruit , $num){ //参数可以为多个
        echo "You want to eat $fruit $num pcs, no problem
    ";
    };
    call_user_func($eat , "apple" , 10); //print: You want to eat apple 10 pcs, no problem;
    call_user_func($eat , "orange" , 5); //print: You want to eat orange 5 pcs,no problem;
    
    // 调用类方法
    class myclass {
        public static function say_hello($name,$message)
        {
            echo "Hello! $name $message";
        }
    }
    
    //array(类名,静态方法名),参数
    call_user_func(array('myclass', 'say_hello'), 'dain_sun', 'good person');
    
    call_user_func_array(array('myclass', 'say_hello'), array('dain_sun', 'good person'));
    
    // Hello! dain_sun good person
    
  • 相关阅读:
    C# 小算法1
    函数 y=x^x的分析
    随机数
    对拍
    Cube Stack
    Permutation
    一笔画问题
    康托展开&&康托逆展开
    待完成
    小错误 17/8/10
  • 原文地址:https://www.cnblogs.com/nixi8/p/6771428.html
Copyright © 2011-2022 走看看