Help us understand the problem. What is going on with this article?

Functoolsで過剰にスマートなPHPを書こう (無名再帰篇)

More than 3 years have passed since last update.

こんにちは! 一年ほど前に作ったPHPのための函数プログラミング用ライブラリTeto Functoolsの利用者が全然増えないので、信者利用者を増やすために、べんり機能を紹介します。

インストール方法

はじめにDownload Composerを読んでcomposerをインストールしてね。

そして、Functoolsをインストール。

composer global require zonuexe/functools

既存のPHPライブラリに組込むにはこうします。

composer require zonuexe/functools

とりあえずglobal指定でインストールしたことを前提にコード書くよ。

無名再帰とは

無名再帰は、無名函数(ラムダ式とも呼ばれる)を使って再帰することだよ。具体的には不動点コンビネータってテクニックを利用するんだ。その中でも一番有名なのが「Yコンビネータ」だよ。ネットビジネスやベンチャーについて興味のあるひとはY Combinatorを知ってるはずだけど、このベンチャーキャピタルを率ゐるポール・グレアムは有名なLisperなんだ。

Haskell/不動点と再帰 - Wikibooksによれば、Haskellではfixって名前らしいから、恥も外聞もなくぱくってきたよ。カッコイイ用語に酔って中二心が溢れ出してきたのでYコンビネータを実装しようと思ったけど、Teto\Functools::fix()で採用されてるのは正確には正格評価の言語でも利用可能なよう変形した「Zコンビネータ」だよ。正格だけにね!

無名再帰のやりかた

PHPでショートコーディングしたいと思ふじゃないですか。

<?php
($f = function ($n, $max) use (&$f){
    if ($n <= $max) {
        printf("%dは%s".PHP_EOL, $n, ($n % 2 === 0) ? "偶数" : "奇数");
        $f($n + 1, $max);
    }
})(1, 20);

↑ このコードはPHP7では動くけど、PHP5系ではSyntax errorなんだ…!

<?php
use Teto\Functools as f;
require_once getenv('HOME') . '/.composer/vendor/autoload.php';

call_user_func(f::fix(function ($f) {
    return function ($n, $max) use ($f){
        if ($n <= $max) {
            printf("%dは%s".PHP_EOL, $n, ($n % 2 === 0) ? "偶数" : "奇数");
            $f($n + 1, $max);
        }
    };
}), 1, 20);

これでPHP5でも式プログラミングができるぞ! ついでに、見すぼらしい$f =も消えて超ハッピー。

無名再帰の活用例

ズンドコキヨシ with PHP - Qiitaを見たら触発されたので書いてみたよ。

<?php
use Teto\Functools as f;
require_once getenv('HOME') . '/.composer/vendor/autoload.php';

call_user_func(f::fix(function ($func) {
    $zundoco = function () {
        static $zundoco = ['ズン', 'ドコ'];
        return $zundoco[mt_rand(0, 1)];
    };

    static $pattern = ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ'];
    return function (array $a) use ($zundoco, $pattern, &$func) {
        $len = count($a);
        if (isset($a[$len - 1])) {
            echo $a[$len - 1], PHP_EOL;
        }
        if ($a === $pattern) {
            echo 'キ・ヨ・シ!', PHP_EOL;
        } else {
            $func(array_merge(
                ((count($pattern) > $len) ? $a : array_slice($a, 1)),
                [$zundoco()]
            ));
        }
    };
}), []);
tadsan
僕に警備する自宅をください。Emacs初心者。Rubyist。 全ての投稿された記事は別段の表記がない限りはCC 3.0 BY-SA https://creativecommons.org/licenses/by-sa/3.0/deed.ja で二次利用できます。 記事中に含まれる全てのコードスニペットの著作権は抛棄するので、煮るなり焼くなりお好きにどうぞ。
https://tadsan.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした