ソースコードサイズ(1944バイト)に最適化されたVerity 0.10
私はもともと質問を誤解し、コードゴルフとして解釈しました。問題の制限の下では、短いオブジェクトコードよりも短いソースコードでクインを書く方がはるかに簡単であるため、これはおそらく最良の結果でした。これにより、質問が簡単になり、私は合理的に答えを出すことができると感じました。また、入力に高水準の言語を使用するように促されました。つまり、プログラム自体で表現を少なくする必要があります。ハードウェア用のゴルフ言語としてVerityを作成しませんでした(実際にはまったく異なるコンテキストで作成するために実際に採用されました)が、かなり記憶があります(たとえば、典型的なHDLよりもかなり高いレベルで、ボイラープレートがはるかに少なく、一般的なHDLよりも移植性が高い)。
短いオブジェクトコードの正しい解決策は、データをある種のツリー構造に格納することを含むと確信しています。問題がブロックROMの使用を許可していないためです。ある時点で、この原則を使用するプログラムを書き始めた可能性があります(どの言語、おそらくVerity、おそらくVerilog、VHDLには定型句が多すぎて、この種の問題に最適ではない可能性があります)。これは、「手動で作成したROM」のすべてのビットにソースコードのすべてのビットを渡す必要がないことを意味します。ただし、Verityコンパイラーは現在、入力の優先順位と結合性に基づいて出力の構造を合成しています。つまり、命令ポインター(したがってルックアップテーブルへのインデックス)を単項で効果的に表現しています。
プログラム自体:
import <print>new x:=0$1296in(\p.\z.\a.new y:=(-a 5-a 1-a 1-a 2-a 4-a 2-a 3-a 2-a 6-a 2-a 0-a 3-a 0-a 4-a 4-a 7-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 6-a 7-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 4-a 3-a 2-a 7-a 5-a 7-a 0-a 6-a 4-a 4-a 1-a 6-a 2-a 6-a 1-a 7-a 6-a 6-a 5-a 1-a 2-a 2-a 0-a 5-a 0-a 0-a 4-a 2-a 6-a 5-a 0-a 0-a 6-a 3-a 6-a 5-a 0-a 0-a 5-a 0-a 6-a 5-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 5-a 3-a 2-a 7-a 5-a 7-a 0-a 5-a 5-a 5-a 1-a 4-a 4-a 3-a 1-a 5-a 5-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 4-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 5-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 4-a 7-a 3-a 6-a 2-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 6-a 3-a 3-a 5-a 1-a 7-a 2-a 6-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 6-a 3-a 1-a 5-a 3-a 7-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 5-a 7-a 5-a 7-a 4-a 6-a 5-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 5-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 4-a 1-a 7-a 7-a 6-a 3-a 7-a 4-a 2-a 0-a 4-a 3-a 6-a 2-a 6-a 3-a 7-a 4-a 2-a 0-a 5-a 4-a 6-a 0-a 7-a 2-a 0-a 1-a 4-a 5-a 3-a 4-a 4-a 4-a 4-a 3-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 3-a 7-a 4-a 2-a 0-a 4-a 4-a 6-a 5-a 6-a 3-a 7-a 5-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 5-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 1-a 5-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 7-a 2-a 7-a 1-a 5-a 1-a 4-a 2-a 3-a 7-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 6-a 6-a 1-a 5-a 1-a 5-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 0-a 5-a 1-a 4-a 4-a 3-a 4-a 4-a 4-a 4-a 6-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 0-a 5-a 0-a 0-a 0-a 1-a 6-a 5-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 2-a 0-a 0-a 1-a 4-a 7-a 4-a 7-a 1-a 6-a 2-a 6-a 1-a 7-a 3-a 6-a 3-a 7-a 0-a 6-a 1-a 5-!x)in while!x>0do(p(if z<32then z+92else z);if z==45then while!y>0do(p 97;p 32;p(48^!y$$3$$32);p 45;y:=!y>>3)else skip;x:=!x>>6))print(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
より読みやすい:
import <print>
new x := 0$1296 in
(\p.\z.\a.
new y := (-a 5-a 1-
# a ton of calls to a() omitted...
-a 1-a 5-!x) in
while !x>0 do (
p(if z<32 then z+92 else z);
if z==45
then while !y>0 do (
p 97;
p 32;
p(48^!y$$3$$32);
p 45;
y:=!y>>3 )
else skip;
x:=!x>>6
)
)(print)(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
基本的な考え方は、データ全体を変数に格納することですx。(通常のクインのように、コードセクションとデータセクションがあります。データはコードのテキストをエンコードし、データのテキストを再生成するために使用することもできます。)残念ながら、Verityでは現在、非常に大きなソースコードで記述される定数(コンパイル中にOCaml整数を使用してソース内の整数を表す。これは、任意の幅の整数型をサポートする言語では明らかに正しくない)–さらに、定数を8進数で指定– x実行時に関数の呼び出しを繰り返しての値を生成しますa。void関数を作成して個別のステートメントとして繰り返し呼び出すこともできますが、その場合、データセクションのテキストの出力を開始する場所を特定するのが難しくなります。そのため、代わりにa整数を返し、算術を使用してデータを格納しました(Verityは算術が左から右に評価されることを保証します)。データセクションはxシングル-サインを使用してエンコードされます。これが実行時に検出されると-a 5-a 1-、を使用してフルに拡張されyます。
のyコピーとして初期化することは、xここではかなり微妙です。a具体的にはゼロを返すため、合計のほとんどはゼロからゼロを差し引いたものであり、それ自体をキャンセルします。私たちは、で終わる!x(すなわち「の値x」;ベリティで、OCamlでのように、変数の名前は、何よりも、ポインタのように動作し、そしてあなたは、変数の値で取得するには、明示的にそれを逆参照する必要があります)。単項マイナスに関するVerityの規則は少し複雑です–単項マイナスvは次のように記述されます(-v)–したがって、(-0-0-0-!x)として解析され(-(0-0-0-!x))、に等しくなり!x、最終的にyのコピーとして初期化されますx。(これは、Verityのであることも注目に値しますありません値による呼び出しですが、関数と演算子が評価する順序を選択できます。-は、右の引数の前に左の引数を評価します。特に、左の引数に副作用がある場合、それらは右の引数が評価されるときに表示されます。)
ソースコードの各文字は、2つの8進数を使用して表されます。つまり、ソースコードは64文字に制限されているため、内部で使用するために独自のコードページを作成する必要がありました。出力はASCIIであるため、内部で変換する必要がありました。これが(if z<32 then z+92 else z)目的です。これが、内部表現で使用した文字セットです(番号順(つまり\、コードポイント0、?コードポイント63))。
\]^_`abcdefghijklmnopqrstuvwxyz{ !"#$%&'()*+,-./0123456789:;<=>?
この文字セットは、Verityにとって重要なほとんどの文字を提供します。欠落している注目すべき文字は次のとおりです}(つまり、を使用してブロックを作成することはできません{}が、すべてのステートメントが式であるため、()代わりに使用できます); そして、|(私は排他的ではなく包含的ORの値を作成し使用していた理由ですxが、私はそれがとにかくいかに大を指定するために必要な、私は0に初期化する必要があるという意味します)。私が文字セットに確実に入れたい重要な文字のいくつかは、<>(インポートの場合、シフトも)、()(これらなしで解析できるプログラムを書くのは非常に難しい)、$(ビット幅を扱うすべての場合)、および\(ラムダの場合;理論的には、これを回避することができますlet…in しかし、それははるかに冗長になります)。
プログラムを少し短くするために、ラムダ引数に一時的にバインドすることにより、printとの略語!x$$6$$32(つまり、の下位6ビット!x、printライブラリで使用できるようにキャスト)を作成しました。
最後に、出力の問題があります。Verityは、printデバッグ出力を目的としたライブラリを提供します。シミュレーターでは、プログラムのテストに完全に使用できる標準出力にASCIIコードを出力します。物理回路基板でprintは、特定のチップとそれを囲むボード用に作成されたライブラリに依存します。printVerityディストリビューションには、7セグメントディスプレイに出力を印刷するためにアクセスした評価ボード用のライブラリがあります。ライブラリが最終的に回路基板のスペースを占有することになるので、この問題の最適化されたソリューションとして別の言語を使用して、出力のビットをワイヤに直接出力できるようにする価値があるかもしれません。
ちなみに、このプログラムはハードウェアではO(n²)です。つまり、シミュレーターの方がはるかに悪いです(O(n⁴)だと思います)。 、そして私がプログラムを書いているときに私の変更に時間がどのように反応したかに基づいて、機能は確かに非常に急速に成長するようです。Verityコンパイラは、プログラムを最適化するために436の最適化パス(通常、それをはるかに超える)を必要とし、その後も、私のラップトップではシミュレーションが非常に困難でした。完全なコンパイルとシミュレーションの実行には、次の時間がかかりました。
real 112m6.096s
user 105m25.136s
sys 0m14.080s
ピーク時のメモリは2740232キロバイトです。プログラムの実行には、合計213646クロックサイクルかかります。それでも機能します!
とにかく、私が間違ったことを最適化していたので、この答えは実際には質問を満たしていませんが、他の答えはまだないので、これはデフォルトで最適です(そして、ゴルフのクインがどのように見えるかを見るのはいいですハードウェア言語)。現在、チップ上でより最適化された出力を生成することを目的としたプログラムに取り組むかどうかは、私にはわかりません。(O(n)データエンコーディングは、ここで見られるものよりもかなり複雑になるため、ソースの点ではるかに大きくなる可能性があります。)