3

I need to realize function "calc" that works like that:

$sum = function($a, $b)  { return $a + $b; };
calc(5)(3)(2)($sum);    // 10
calc(1)(2)($sum);       // 3
calc(2)(3)('pow');      // 8

I can write something like this:

function calc(){;
    print_r(func_get_args());
    return __FUNCTION__;
}
calc(3)(5)(2)('sum');

and it print Array ( [0] => 3 ) Array ( [0] => 5 ) Array ( [0] => 2 ) Array ( [0] => sum ).

So, when I get 'sum' in my function, i should have an array with all previous arguments. But i have no idea, how can i pass current argument in next function call to manipulate all of them on last iteration. Or is there some sort of recursive solution?

  • make class with static function and static stack array. And calculate, when string given – splash58 Oct 03 '18 at 12:38
  • calc(2)(3)('pow'); cant be a function... the parameters of the function must be inside the () and not each parameter on different (): you can do a function for example to calculate the power by passing three parameters like this: function ($nr1, $nr2, $operation) {} – Sigma Oct 03 '18 at 12:39
  • This is possible in PHP, but it isn't a nice solution. – Mikey Oct 03 '18 at 12:52

3 Answers3

4

What you're talking about is called Currying. The following code will require PHP 7, since it involves invoking a function returned from another one, which wasn't possible until PHP's Abstract Syntax Tree was implemented in that version.

First things first, you'll need a new sum() function that can operate on an arbitrary number of variables:

$sum = function(...$args)  { return array_sum($args); };

Secondly, the important part. A function that returns a new anonymous function, accumulating the arguments as it goes. When you finally pass it something callable (either your $sum function, or a built-in function name like pow), it'll execute it, unpacking the arguments that it's built up.

function calc($x)
{
    return function($y = null) use ($x)
    {
        if (is_callable($y)) {
            return $y(...$x);
        } else {
            $args = (array) $x;
            $args[] = $y;
            return calc($args);
        }
    };
}

echo calc(5)(3)(2)($sum); // 10
echo calc(1)(2)($sum); // 3
echo calc(2)(3)('pow'); // 8

See https://3v4l.org/r0emm

(Note that internal functions will be limited to operating on the number of arguments they are defined to take - calc(2)(3)(4)('pow') will raise an error.)

This isn't a particularly common pattern to use (which is probably why you've found it hard to track down), so please for everyone who reads it's sake, think carefully about where you use it.

Credit to the curryAdd answer in this question for the starting blocks.

iainn
  • 16,826
  • 9
  • 33
  • 40
2

Edit: I stand corrected, you don't require globals it seems! Definitely use the @iainn's answer over this one.


So to achieve this you're going to have to use globals if you're not doing it within a class to maintain current state. You can see a working example of the below code here (note that it only works for PHP version 7 and above)

<?php

$sum = function(...$args) {
    return array_sum($args);
};

function calc(...$args) {
    global $globalArguments;
    if (is_callable($args[0])) {
        $callback = $args[0];
        $arguments = array_map(function ($arg) {
            return $arg[0];
        }, $globalArguments);

        return $callback(...$arguments);
    }
    $globalArguments[] = $args;
    return __FUNCTION__;
}

echo calc(3)(2)($sum); // 5

I don't know why you want to do this, but I don't suggest it in production, globals aren't something that should really be used if you can avoid it.

Mikey
  • 2,606
  • 1
  • 12
  • 20
0
function calc(int $value, Callable $function = null)
{
    return function ($v) use ($value, $function) {
        $f = function ($call) use ($value, $function) {
            return (is_callable($call) && is_callable($function)) ? $call($function($call), $value) : $value;
        };
        return is_callable($v) ? $f($v) : calc($v, $f);
    };
}