5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PHPのmatch式に絶望したからネタでC++にmatch式を実装する

Last updated at Posted at 2022-06-16

はじめに

※ ネタです

PHP の match 式 を読んでいたら、何やら臭うコードが・・・

function fizzbuzz($num) {
    print match (0) {
        $num % 15 => "FizzBuzz" . PHP_EOL,
        $num % 3  => "Fizz" . PHP_EOL,
        $num % 5  => "Buzz" . PHP_EOL,
        default   => $num . PHP_EOL,
    };
}

この match (0) 部分、なんか気持ち悪くないですか?

$num の値に応じて処理を分岐させたいので、ここでは match ($num) と書けるのが美しいと感じました。

この気持ち、C++ にぶつけよう!

C++

ご存知 C++ には演算子オーバーロードがあり、言語の許す限り任意の構文っぽいものを生み出せます。

これが C++ の match 式だ!

case の条件部分に match (n)n を引数としたラムダ式を書けるところがポイントです。

for (unsigned n = 1; n <= 20; ++n)
{
    auto const s = match (n) [
        (arg % 15 == 0) --> "FizzBuzz",
        (arg %  3 == 0) --> "Fizz",
        (arg %  5 == 0) --> "Buzz",
        default_ --> std::to_string(n)
    ];
    std::cout << s << std::endl;
}
コード

:warning: グローバルに制約のない演算子オーバーロードを配置するなど、汎用性は一切考慮されていないものです

#include <iostream>
#include <string>
#include <tuple>

template <typename L, typename R>
struct modulo_expression
{
    L lhs;
    R rhs;

    template <typename A>
    auto eval(A arg) const
    {
        return lhs.eval(arg) % rhs.eval(arg);
    }
};

template <typename L, typename R>
struct equals_expression
{
    L lhs;
    R rhs;

    template <typename A>
    auto eval(A arg) const
    {
        return lhs.eval(arg) == rhs.eval(arg);
    }
};

template <typename T>
struct identity_expression
{
    T value;

    template <typename A>
    T eval(A) const
    {
        return value;
    }
};

struct argument_expression
{
    template <typename A>
    A eval(A arg) const
    {
        return arg;
    }
};

argument_expression const arg;

template <typename L, typename R>
modulo_expression<L, identity_expression<R>> operator % (L lhs, R rhs)
{
    return { lhs, { rhs } };
}

template <typename L, typename R>
equals_expression<L, identity_expression<R>> operator == (L lhs, R rhs)
{
    return { lhs, { rhs } };
}

template <typename E>
struct pattern_expression
{
    E expression;

    template <typename A>
    auto eval(A arg) const
    {
        return expression.eval(arg);
    }
};

template <typename E>
pattern_expression<E> operator -- (E expression, int)
{
    return { expression };
}

template <typename P, typename T, typename N>
struct case_expression
{
    P pattern;
    T value;
    N next;

    template <typename A>
    std::tuple<bool, T> eval(A arg) const
    {
        auto const t = next.eval(arg);
        if (std::get<0>(t))
        {
            return t;
        }
        else
        {
            return std::make_tuple(pattern.eval(arg), value);
        }
    }
};

template <typename P, typename T>
struct case_expression<P, T, void>
{
    P pattern;
    T value;

    template <typename A>
    std::tuple<bool, T> eval(A arg) const
    {
        return std::make_tuple(pattern.eval(arg), value);
    }
};

template <typename P, typename T>
case_expression<P, T, void> operator > (P pattern, T value)
{
    return { pattern, value };
}

template <typename P1, typename T1, typename N1, typename P2, typename T2>
case_expression<P2, T2, case_expression<P1, T1, N1>> operator , (case_expression<P1, T1, N1> lhs, case_expression<P2, T2, void> rhs)
{
    return { rhs.pattern, rhs.value, lhs };
}

struct default_pattern_expression
{
    template <typename A>
    auto eval(A) const
    {
        return true;
    }
};

default_pattern_expression const default_{};

template <typename S>
struct match_expression
{
    S scrutinee;

    template <typename C>
    auto operator [] (C cases) const
    {
        return std::get<1>(cases.eval(scrutinee));
    }
};

template <typename S>
match_expression<S> match(S scrutinee)
{
    return { scrutinee };
}

int main(void)
{
    for (unsigned n = 1; n <= 20; ++n)
    {
        auto const s = match(n) [
            (arg % 15 == 0) --> "FizzBuzz",
            (arg %  3 == 0) --> "Fizz",
            (arg %  5 == 0) --> "Buzz",
            default_ --> std::to_string(n)
        ];
        std::cout << s << std::endl;
    }

    return 0;
}

おわりに

数年ぶりに書きましたが、やっぱ C++ は楽しいですね〜

今回 std::to_string の存在を知りました。便利になりましたね。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?