;
f=
g
ij=f
a =hi
hi = g
hij= ij
g ' ' =0
g '"' =0;
g '$' =0;
g '&' =0-0
g '(' =0-0-0
g '*' =0-0-0;
g ',' =0-0-0;
g '.' =0-0-0-0
g '0' =0-0-0-0-0
g '2' =0-0-0-0-0;
g '4' =0-0-0-0-0;
g '6' =0; g '8' =0
g ':' =0; g '<' =0-0
g '>' =0; g '@' =0-0;
g 'B' =0; g 'D' =0-0;
g 'F' =0; g 'H' =0-0-0
g 'J' =0; g 'L' =0-0-0-0
g 'N' =0; g 'P' =0-0-0-0;
g 'R' =0; g 'T' =0-0-0-0;
g 'V' =0; g 'X' =0-0-0-0-0
g 'Z' =0; g '^' =0; g '`' =0
g 'b' =0; g 'd' =0; g 'f' =0;
g 'h' =0; g 'j' =0; g 'l' =0;
g 'n' =0; g 'p' =0; g 'r' =0-0
g 't' =0; g 'v' =0; g 'x' =0-0-0
g 'z' =0; g '\92' =0-0; g '|' =0;
g '~' =0; g y = 1 ;z=0; i(-0)z=z;
i m('\10':y ) ="y"; ; ; ; ; ; ; ;
i m(mnmnmnmnm:y ) = i(m - 1 ) y ; ;
i k m ="y"; ; k i [ ] =01<1010101010;
k m('\10':y ) = k(m + 1 )(i m y ) ; ;
k m y =01>10; m o = k 1$'\10':o ; ; ;
o i('\10':y ) = o i y ; ; ; ; ; ; ; ; ;
o i(k:y )|g k<i = o(1 - i ) y ; ; ; ; ; ;
o i(k:y )|g k>i = o(1 - i ) y ; ; ; ; ; ;
o i [ ] =01<10; o i y =01>10;v=01>10101010
s y|o 1 y = m y|o(-0) y = m y ; s y =v; ; ;
オンラインでお試しください!
説明
これはHaskellにとって非常に興味深いタスクでした。
パリティ
開始するには、文字が偶数か奇数のコードポイントを持っているかどうかを判断するいくつかの方法が必要です。これを行う通常の方法は、コードポイントを取得して2で変更することです。ただし、ご存知のように、文字のコードポイントを取得するにはインポートが必要です。これは、ソースの制限により、中古。より経験のあるHaskellerは再帰を使用することを考えます。 Char
は型Enum
クラスの一部なので、その前任者と後継者を取得できます。ただしpred
、succ
バイトパリティを代替しないため、どちらも使用できません。
そのため、文字で操作することはほとんどできないため、かなり行き詰まっています。これに対する解決策は、すべてをハードコーディングすることです。私たちは(ほとんど)偶数の文字をリテラルとして表す'
ことができます。奇数なので奇妙です。文字自体の隣に置くことはできないため、ほとんどの奇数の文字をリテラルで表現することは不可能です。そのため、すべての偶数バイトをハードコードし、最後に奇数バイトのキャッチオールを追加します。
問題のバイト
単一引用符で囲むことによってリテラルを作成できないいくつかの偶数バイトがあることに気付くかもしれません。それらは、印刷不能、改行、および\
です。unprintablesを使用しない限り、検証する必要がないため、unprintablesについて心配する必要はありません。実際、タブなどの奇妙な印刷不可をまだ使用できますが、必要がなくなるだけです。とにかくプログラムから削除されるため、改行は賢く無視できます。(コードポイントはかなり便利なので、改行を含めることもできますが、必要はありません)。これはを残し\
、\
コードポイント92を持ちます。これは便宜的に奇数の後に偶数が続くため、偶数と奇数を\92
交互に繰り返すため、リテラル'\92'
完全に有効です。後で改行を表す必要がある場合は、幸いにも同じプロパティがあることに気づくでしょう'\10'
。
間隔の問題
次に、実際のコードの作成を開始するには、かなりの数の文字を1行に配置できる必要があります。これを行うために、私はキャップを書きました:
;
f=
g
ij=f
a =hi
hi = g
hij= ij
キャップは有効なHaskell以外には何もしません。私は当初、後でコードで役立つ定義を作成することを望んでいましたが、そうではありませんでした。空白やセミコロンなど、キャップを作成する簡単な方法もありますが、この方法ではバイトが節約されないので、変更する必要はありません。
ハードコーダー
行に十分なスペースができたので、値のハードコーディングを開始します。これはほとんど退屈ですが、いくつか興味深い点があります。1つは、行がさらに長くなり始めると、1行;
に複数の宣言を配置するために使用できるため、大量のバイトを節約できます。
2つ目は、常に行g
ごとに行を開始できるとは限らないため、行を少しインデントする必要がある場合です。Haskellはインデントを本当に気にしているので、これについて文句を言うでしょう。ただし、インデントされた行の前の最後の行がセミコロンで終わっている場合は、許可されます。どうして?私は最弱者ではありませんが、うまくいきます。したがって、セミコロンを行の最後に置くことを忘れないでください。
機能ビルディングブロック
ハードコーダーが完了すると、プログラムの最後までスムーズに進みます。いくつかの単純な関数を作成する必要があります。最初に私はのバージョン構築drop
と呼ばれるが、i
。 文字列の末尾を超えてドロップしようとすると、それだけが返されるというi
点が異なりdrop
ます"y"
。i
ドロップとは異なり、改行をドロップしようとすると、が返されます"y"
。これらは後でプログラムが三角形であることを確認しているFalse
ときに、最後の行が完了していないとき、または行は早く終わります。
k
k
nssTrue
nk
n+1False
私たちは、その後のエイリアスを作成しk
、m
。m
は最初の引数だけk
であり1
、2番目の引数の前に改行が追加されます。
次はありo
ます。 o
数値と文字列を受け取ります。文字列バイト(改行を無視)g
が入力番号で始まる(を使用して)パリティーで交互に入れ替わるかどうかを決定します。
最後に、との両方s
で実行さo
れるものが1
あり0
、どちらかが成功した場合はに延期されm
ます。両方に失敗した場合は、単に戻りますFalse
。これが必要な機能です。入力が三角形で交互になっていると判断します。