6

範囲に対してマップ/フォールド操作を実行できるライブラリを作成しています。これらは演算子で行う必要があります。私は関数型プログラミングにあまり精通しておらず*、マップと||フォールドを暫定的に選択しました。cos(x)したがって、間隔内の最大値(ブルートフォースアルゴリズム)を見つけるには 8 < x < 9::

double maximum = ro::range(8, 9, 0.01) * std::cos  || std::max;

上記でro::rangeは、任意のSTLコンテナに置き換えることができます。

マップ/フォールド演算子の規則がある場合、私は違いたくありません。私の質問は次のとおりです。数学表記はありますか、それともマップ/フォールドに演算子を使用する言語はありますか?

** 編集 **

質問した人のために、以下はROが現在できることの小さなデモです。 sccC++スニペットを評価できる小さなユーティリティです。

// Can print ranges, container, tuples, etc directly (vint is vector<int>) :
scc 'vint V{1,2,3};  V'
{1,2,3}

// Classic pipe. Alogorithms are from std::
scc 'vint{3,1,2,3} | sort | unique | reverse'
{3, 2, 1}

// Assign 42 to [2..5)
scc 'vint V=range(0,9);   range(V/2, V/5) = 42;  V'
{0, 1, 42, 42, 42, 5, 6, 7, 8, 9}


// concatenate vector of strings ('add' is shotcut for std::plus<T>()):
scc 'vstr V{"aaa", "bb", "cccc"};  V || add'
aaabbcccc

// Total length of strings in vector of strings
scc 'vstr V{"aaa", "bb", "cccc"};  V * size ||  (_1+_2)'
9

// Assign to c-string, then append `"XYZ"` and then remove `"bc"` substring :
scc 'char s[99];  range(s) = "abc";  (range(s) << "XYZ") - "bc"'
aXYZ


// Remove non alpha-num characters and convert to upper case
scc '(range("abc-123, xyz/") | isalnum) * toupper'
ABC123XYZ


// Hide phone number:
scc "str S=\"John Q Public  (650)1234567\";  S|isdigit='X';  S"
John Q Public  (XXX)XXXXXXX
4

3 に答える 3

11

これは本当の答えというよりは実際にはコメントですが、コメントに収めるには長すぎます。

少なくとも、用語の私の記憶が正しく機能する場合、マップは本質的std::transformにであり、フォールドはstd::accumulateです。それが正しいと仮定すると、自分で書くことはせいぜいお勧めできません。

マップ/フォールドスタイルのセマンティクスを使用する場合は、次のようにすることができます。

std::transform(std::begin(sto), std::end(sto), ::cos);
double maximum = *std::max_element(std::begin(sto), std::end(sto));

std::accumulate汎用のフォールドに似ていますstd::max_elementが、基本的にはfold(..., max);次のようになります。単一の操作が必要な場合は、次のように実行できます。

double maximum = *(std::max_element(std::begin(sto), std::end(sto),
    [](double a, double b) { return cos(a) < cos(b); });

この目的のために、オーバーロード演算子を再検討することをお勧めします。私が上で示したどちらの例も、ほとんどすべての合理的なC++プログラマーにとって明らかなはずです。あなたが与えた例は、ほとんどの人にとって完全に不透明です。

より一般的なレベルでは、演算子をオーバーロードするときは細心の注意を払う必要があります。演算子のオーバーロードは、正しく使用すると優れています。任意精度の整数、行列、複素数などの演算子をオーバーロードできるため、これらの型を使用するコードは、オーバーロードされた演算子のないコードよりもはるかに読みやすく、理解しやすくなります。

残念ながら、予期しない方法で演算子を使用する場合、正反対のことが当てはまります。これらの使用は確かに非常に予期しないものです。実際、「非常に驚くべき」範囲に収まります。これらの演算子が特定の領域で十分に理解されているが、C ++の他の使用法とは反対であるかどうかは、疑問があるかもしれません(ただし、少なくとも少し正当化されます)。ただし、この場合、「布全体から」という表記法を発明しているようです。C++がオーバーロードをサポートして、折り畳みまたはマップ(または他の視覚的に類似または類似しているもの)を意味する演算子を使用している人は誰も知りません。仕方)。要するに、このようにオーバーロードを使用することは、不十分で不当な考えです。

于 2012-12-10T06:58:36.293 に答える
5

私が知っている言語の中で、折りたたむための標準的な方法はありません。Scalaは演算子/::\metthod名を使用し、Lispはreduce、Haskellはを使用しfoldlます。

map一方map、私が知っているすべての言語のように、単純に見つけるのがより一般的です。

于 2012-12-10T06:05:49.397 に答える
2

以下は、fold準人間が読める形式の中置C++構文の実装です。コードはそれほど堅牢ではなく、要点を示すためだけに役立つことに注意してください。これは、より一般的な3引数fold演算子(範囲、2項演算、および中立要素)をサポートするために作成されています。

これは簡単に、オペレーターの過負荷を悪用する(「レイプ」と言ったことがありますか?)おかしな方法であり、900ポンドの砲弾で自分を足で撃つための最良の方法の1つです。

    enum { fold } fold_t;

template <typename Op>
struct fold_intermediate_1
{
    Op op;
    fold_intermediate_1 (Op op) : op(op) {}
};

template <typename Cont, typename Op, bool>
struct fold_intermediate_2
{
    const Cont& cont;
    Op op;
    fold_intermediate_2 (const Cont& cont, Op op) : cont(cont), op(op) {}
};

template <typename Op>
fold_intermediate_1<Op> operator/(fold_t, Op op)
{
    return fold_intermediate_1<Op>(op);
}

template <typename Cont, typename Op>
fold_intermediate_2<Cont, Op, true> operator<(const Cont& cont, fold_intermediate_1<Op> f)
{
    return fold_intermediate_2<Cont, Op, true>(cont, f.op);
}

template <typename Cont, typename Op, typename Init>
Init operator< (fold_intermediate_2<Cont, Op, true> f, Init init)
{
    return foldl_func(f.op, init, std::begin(f.cont), std::end(f.cont));
}

template <typename Cont, typename Op>
fold_intermediate_2<Cont, Op, false> operator>(const Cont& cont, fold_intermediate_1<Op> f)
{
    return fold_intermediate_2<Cont, Op, false>(cont, f.op);
}

template <typename Cont, typename Op, typename Init>
Init operator> (fold_intermediate_2<Cont, Op, false> f, Init init)
{
    return foldr_func(f.op, init, std::begin(f.cont), std::end(f.cont));
}

foldr_funcおよびfoldl_func(左右の折り畳みの実際のアルゴリズム)は他の場所で定義されています。

次のように使用します。

foo myfunc(foo, foo);
container<foo> cont;
foo zero, acc;

acc = cont >fold/myfunc> zero; // right fold
acc = cont <fold/myfunc< zero; // left fold 

この言葉foldは、ここでは貧乏人の新しい予約語の一種として使用されています。この構文のいくつかのバリエーションを定義できます。

<<fold/myfunc<< >>fold/myfunc>>
<foldl/myfunc> <foldr/myfunc>
|fold<myfunc| |fold>myfunc|

内側の演算子は、外側の演算子と同じかそれ以上の優先順位を持っている必要があります。これはC++文法の制限です。

の場合map、必要な中間体は1つだけで、構文は次のようになります。

mapped = cont |map| myfunc;

それを実装するのは簡単な練習です。

ああ、そして、あなたが何をしているのかをよく知っているのでない限り、そしておそらくあなたがそうしているとしても、この構文を本番環境で使用しないでください;)

于 2012-12-10T10:31:39.543 に答える