3

マルチクインを次のように定義しています。

入力が与えられない場合、それぞれが正確なソースコードを出力し、入力としてnが与えられる場合、*n* 番目のプログラムのソースコードを出力する、n 個の異なるプログラミング言語によるn 個のプログラムのセット。

これは、最初のプログラムが出力されるまで、各プログラムが次のソース コードを出力する、プログラムの循環シーケンスと混同しないでください。この場合、各プログラムはクインではなく、ポイントを無効にします。これらの巡回セットは、 nの値が大きい場合に興味深い頭の体操ですが、実装するのは非常に簡単です。

この場合、複合とは「nの値2以上の場合」を意味します。この場合、 の解決策n = 2は十分に複雑であると思います。ただし、 nのすべての値に対する一般的な解決策 (読み方: 戦略) が目標です。

「単純な」クインがどのように書かれているかは理解していますが、私を魅了する複雑なマルチクインについて頭を悩ませているようには見えません。私の一部は、プログラマーの頭の中にあるせん断の創意工夫以外に解決策がないことを望んでいますが、これはありそうもないことだと思います。

4

1 に答える 1

4

クワイン、多言語クワイン、マルチクワインには特別なものは何もありません。それらはすべてほとんど自動的に書き込むことができます。

これは、例として、C ++の標準的で、冗長で、エレガントでなく、非効率的なクワインです。ただし、すべての欠点があるため、必要な操作を行うために変更するのは非常に簡単です。

#include <iostream>
#include <string>
#include <cstdlib>

std::string show (const std::string& in) {
    std::string res = "\"";
    for (std::string::const_iterator it = in.begin(); it < in.end(); ++it) {
        switch (*it) {
            case '"':
            case '\\':
                res += '\\';
            default:
                res += *it;
        }
    }
    res += "\"";
    return res;
}

int main (int argc, char* argv[])
{
    std::string arr[] = { // beginning ends here
"#include <iostream>",
"#include <string>",
"#include <cstdlib>",
"",
"std::string show (const std::string& in) {",
"    std::string res = \"\\\"\";",
"    for (std::string::const_iterator it = in.begin(); it < in.end(); ++it) {",
"        switch (*it) {",
"            case '\"':",
"            case '\\\\':",
"                res += '\\\\';",
"            default:",
"                res += *it;",
"        }",
"    }",
"    res += \"\\\"\";",
"    return res;",
"}",
"",
"int main (int argc, char* argv[])",
"{",
"    std::string arr[] = { // beginning ends here",
"======",
"    };",
"    int n = argc == 1 ? 0 : std::atoi(argv[1]);",
"    if (n == 0) {",
"        int i, j;",
"        for (i = 0; arr[i] != \"======\"; ++i) std::cout << arr[i] << std::endl;",
"        for (j = 0; j < sizeof(arr)/sizeof(arr[0]); ++j) std::cout << show(arr[j]) << ',' << std::endl;",
"        for (++i; i < sizeof(arr)/sizeof(arr[0]); ++i) std::cout << arr[i] << std::endl;",
"    } else {",
"    }",
"}",
    };
    int n = argc == 1 ? 0 : std::atoi(argv[1]);
    if (n == 0) {
        int i, j;
        for (i = 0; arr[i] != "======"; ++i) std::cout << arr[i] << std::endl;
        for (j = 0; j < sizeof(arr)/sizeof(arr[0]); ++j) std::cout << show(arr[j]) << ',' << std::endl;
        for (++i; i < sizeof(arr)/sizeof(arr[0]); ++i) std::cout << arr[i] << std::endl;
    } else {
    }
}

ご覧のとおり、プログラムの中心は、と呼ばれる小さな関数です。この関数showは、文字列を受け取り、その表現をC++リテラルとして返します。全体的な構造は次のとおりです。文字列配列の開始部分を出力します。パイプされた配列全体を印刷しshowます; 配列の最後の部分を印刷します。文字列配列はプログラムのコピーであり、プログラムの途中に挿入されます。"====="最初の部分は、プログラムからコピーされない特別な文字列によって最後の部分から分離されます(これは、を介して1回だけ印刷されますshow)。

別のクインを別の言語で印刷するなど、追加のアクションを簡単に挿入できます。そのようなアクションのプレースホルダーを挿入しました。

今では、これを任意のプログラミング言語(たとえば、FORTRAN)に翻訳することは絶対に簡単です。これを実行し、行L1、L2、...、LNで構成されているとします。これらのステートメントをプレースホルダーに挿入します。

std::cout << "L1" << std::endl;
std::cout << "L2" << std::endl;
...
std::cout << "LN" << std::endl;

それに応じて文字列配列を変更します。Voilà、コマンドライン引数に応じて、それ自体を印刷できるクインと、FORTRANのクインがあります。

OK、FORTRANクワインはどうですか?C ++クワインではなく、それ自体を印刷することしかできません。問題ありません。C++クインをFORTRANクインにコピーして戻しましょう。

しかし、C ++クワインには、すでにFORTRANクワイン全体が2回含まれていますか?

FORTRANクワインはすでにそれ自体を印刷できるので、問題ありません。したがって、元のC++行をFORTRANにコピーして戻すだけで済みます。FORTRANをそれ自体の中でもう一度(または2回)複製する必要はありません。

FORTRANを少し変更するだけです。FORTRANクインにC++クインを印刷するように依頼すると、C ++クインと同じように、すべてのC++行とすべてのFORTRAN行を2回印刷する必要があります。次に、C ++クワイン(FORTRANクワインを含む)を元に戻します。Listd::cout << "Li" << std::endl;

また、これらのFORTRANの変更をC ++に戻す必要があります(つまり、std::cout << "Li" << std::endl;行を変更します)。そして、変更の波はここで止まります。

これで、コマンドライン引数に応じて、それ自体または相互に出力できる2つのプログラムがあります。

実際にこれをすべて行うことをお勧めします。

于 2012-12-14T00:33:58.180 に答える