2017-11-27 2 views
7

Ich möchte von verringern/fold eine Zusammensetzung Funktion für n-Funktionen ableiten, aber es funktioniert nicht wie erwartet:Warum gibt meine Funktionszusammensetzung, die durch Reduzieren implementiert wird, eine Schließung zurück?

$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 

Ich habe die gleiche Funktion in Javascript umgesetzt und es funktioniert. Ich benutze PHP 7.0.8 cli. Ich weiß nicht viel über PHP, also übersehe ich wahrscheinlich etwas.

+0

Funktionale Programmierung ruft nicht gut in PHP nicht das Gefühl, als es nicht gut fühlen in JavaScript entweder. Diese Sprachen waren einfach nicht für FP gedacht. Es kann getan werden, wie Sie bereits herausgefunden haben, aber es ist eine Menge Code, der eine Menge Tricks erfordert, um es in die gewünschte Richtung zu schieben. Es ist schön wie eine Übung, aber ich würde es nicht in die Produktion verwenden. Zumindest in PHP ist eine äquivalente OOP-Lösung (wahrscheinlich mit [fluent interface] (https://martinfowler.com/bliki/FluentInterface.html) besser lesbar und bietet, mehr oder weniger, die nette API, die Sie wünschen. – axiac

Antwort

2

Ihre $comp ist curried und natürlich Sie entdeckt PHP native array_reduce erwartet, dass die Funktion mehrere Parameter akzeptiert - eine schnelle Anwendung von uncurry dauert einige Ihrer Schmerzen entfernt, aber Sie müssen weiterlesen, wenn Sie sehen möchten, wie dies insgesamt verbessert werden kann ...


in bescheidenen Meinung nach PHP ...

Mit uncurry funktioniert der Trick, aber Sie werden wahrscheinlich Ihr Programm am Ende nicht zu mögen, wenn alle Funktionen als $ -named Variablen definiert sind - Ich sehe viele kleine Probleme mit diesem Stil.

PHP hat einen callable „Typ“, die Dinge ein wenig mehr PHP-ish macht - benutzerdefinierte Funktionen (einschließlich Funktionen höherer Ordnung) sollten mit call_user_func und 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); 
    }; 
} 

jetzt mit Ihrem heißen compn variadische Schnittstelle arbeitet als

erwartet
function compn (...$fs) { 
    return fold ('comp', 'identity') ($fs); 
} 

function inc ($x) { 
    return $x + 1; 
} 

echo compn ('inc', 'inc', 'inc') (0); // 3 

Aber es funktioniert mit anonymen Funktionen zu


Funktionscode, modulares Programm

Mit Ihrer Funktionen deklariert function Syntax verwenden, können Sie sie in andere Bereiche des Programms

// 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}; 

Und jetzt Sie don import Wenn Sie eine Ihrer Funktionen verwenden möchten, müssen Sie use -Blöcken mit Code

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

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

Wenn es zu debuggen kommt, zweifellos die namens Funktionen bieten hilfreiche Stack-Trace-Nachrichten als auch


relevant, aber unwichtig

PHP hat andere Gründe für das Hinzufügen der aufrufbare Art, aber diejenigen, die ich sicher bin, die nicht betreffen Sie wie sie sind OOP-bezogene - zB

Klasse Methode ruft

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

Objektmethode

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

Don ' Versteh mich falsch, der aufrufbare "Typ" von PHP ist ekelhaft - er erinnert an JavaScript "setTimeout", das eine Zeichenfolge akzeptiert, die zum Suchen einer Funktion in der globalen Umgebung verwendet werden kann - PHP erlaubt es nicht, benannte Funktionen als Argumente zu übergeben andere Funktionen, so dass ich keinen praktischeren Weg dafür kenne – naomik

+0

Der aufrufbare PHP-Typ akzeptiert auch Namespaces in den Zeichenfolgenqualifikatoren lol - zB ''my \ module \ foo'' - aber pass auf, wenn du es benutzt doppelte Anführungszeichen! Sie müssen die Schrägstriche "" mein \\ Modul \\ foo "' - Gotcha nach Gotcha – naomik

+0

_you wird wahrscheinlich am Ende Ihr Programm ablehnen, wenn alle Funktionen definiert sind als $ -named Variablen_ Jaaa, tue ich. Aber Funktionen als Strings sind a wkward auch: 'compn ($ double, $ double, 'inc')'. Wie auch immer, das hat sehr geholfen, danke! – ftor

2

Ich fand es heraus: array_reduce Anrufe $f als Multi-Argument-Funktion. Also muss ich eine andere anonyme Funktion vorstellen:

$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 

Und hier ist die Verringerung/fold-Wrapper eine schönere API zu erhalten:

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

$compn($inc, $inc, $inc) (0); 
Verwandte Themen