QBasicでゴルフをするための一般的なヒントは何ですか?私は、少なくともQBasicに特有のゴルフ問題全般のコーディングに適用できるアイデアを探しています(たとえば、「コメントの削除」は答えではありません)。
QB64エミュレーターに関するヒントも歓迎します。Microsoft QBasicにはない追加機能がいくつかあります。
QBasicでゴルフをするための一般的なヒントは何ですか?私は、少なくともQBasicに特有のゴルフ問題全般のコーディングに適用できるアイデアを探しています(たとえば、「コメントの削除」は答えではありません)。
QB64エミュレーターに関するヒントも歓迎します。Microsoft QBasicにはない追加機能がいくつかあります。
回答:
QBasicはは、いくつかのループ構造を持っていますFOR ... NEXT
、WHILE ... WEND
とDO ... LOOP
。GOTO
または(場合によっては)RUN
ループすることもできます。
FOR ... NEXT
それが何をするかでかなり良いです。Pythonとは異なり、ほとんどの場合、同等のWHILE
or GOTO
ループよりも短くなります。
FOR i=1TO 19STEP 2:?i:NEXT
i=1:WHILE i<20:?i:i=i+2:WEND
i=1:9?i:i=i+2:IF i<20GOTO 9
の後NEXT
に変数名を繰り返す必要はありません。また、数字とそれに続くほとんどのキーワードの間のスペースを削除できることに注意してください。
WHILE ... WEND
0回実行する必要があるループがある場合に適しています。ただし、ループが少なくとも1回実行されることがわかっている場合は、GOTO
1バイト短くなる可能性があります。
WHILE n>1:n=n\2:WEND
1n=n\2:IF n>1GOTO 1
DO ... LOOP
無限ループにのみ使用します(RUN
代わりに使用できる場所を除く)。無条件と同じ数の文字がかかりますが、GOTO
読む方がもう少し直感的です。(「無限ループ」には、aの使用から抜け出すループを含めることができますGOTO
。)DO WHILE
/ DO UNTIL
/ LOOP WHILE
/ LOOP UNTIL
構文は冗長すぎます。WHILE
または必要GOTO
に応じて使用することをお勧めします。GOTO
上記のように、do / whileループを記述する最も一般的な方法です。ラベルの代わりに1桁の行番号を使用します。ステートメントGOTO
のTHEN
一部にa のみが含まれている場合、IF
2つの等しく簡潔なショートカット構文が使用できることに注意してください。
IF x>y GOTO 1
IF x>y THEN 1
GOTO
また、より複雑な制御フローを作成するために使用できます。否定論者はこれを「スパゲッティコード」と呼んでいますが、これはコードゴルフです。読み難さはほとんど美徳です。GOTO
誇り!
RUN
プログラム内の特定の場所にジャンプする必要があり、変数の値を保持する必要がない場合に便利です。RUN
単独でプログラムを上部から再起動します。ラベルまたは行番号を使用すると、その行から再開されます。主にステートレスの無限ループを作成するために使用しました。ある整数が別の整数で割り切れるかどうかをテストする必要があるプログラムでは、明白な方法は以下を使用することMOD
です:
x MOD 3=0
しかし、より短い方法は整数除算を使用することです:
x\3=x/3
つまり、x
int-div 3
はx
float-div と等しくなり3
ます。
これらのアプローチはどちらも0
偽りと真実を返すため-1
、結果を否定するか、加算する代わりに減算する必要があることに注意してください。
逆の条件が必要な場合(つまりx
で割り切れない場合3
)、明らかなアプローチは不等号演算子を使用することです:
x\3<>x/3
しかし、x
負でないことが保証されている場合は、バイトを保存できます。整数除算では結果が切り捨てられるため、常に浮動小数点除算以下になります。したがって、条件は次のように記述できます。
x\3<x/3
同様に、x
負であることが保証されている場合、切り捨てにより結果が増加するため、と書くことができますx\3>x/3
。の兆候がわからない場合x
は、固執する必要があります<>
。
多くの言語と同様に、削除できる文字と削除できない文字を知ることが重要です。
IF""=a$THEN?0
FOR i=1TO 10STEP 2
。QBasic 1.1(archive.orgで入手可能)とQB64にはいくつかの違いがあります。
?123x
なりPRINT 123; x
。上記の例外は、1e2
やなどのシーケンスです1d+3
。これらは科学表記法として扱われ、100!
そして1000#
(それぞれ、シングルおよび倍精度)。d
、e
またはf
それらは、十分に形成された科学的表記リテラルの一部である場合を除き、すべてで。(たとえば、行番号の後にスペースを省略することができない1 FOR
か、9 END
あなたが適切なQBasicをでできるように、。)print文でこれだけ推論のセミコロン表現の一つは、文字列の場合:?123"abc"
作品ではなく、?TAB(5)123
か?123x
。PRINT
をTAB
orの呼び出しで終わるステートメントに追加しますSPC
。(QB64はサポートしていません。)0
小数点(.1
または1.
)の前後で省略できますが、両方(.
)はできません。ENDIF
はと同等END IF
です。Next
ステートメントを組み合わせるNext:Next:Next
まで凝縮される可能性があります
Next k,j,i
どこのためのイテレータFor
ループがありi
、j
とk
-の順で。
たとえば、以下(69バイト)
Input n,m,o
For i=0To n
For j=0To m
For k=0To o
?i;j;k
Next
Next
Next
65バイトまで圧縮可能
Input n,m,o
For i=0To n
For j=0To m
For k=0To o
?i;j;k
Next k,j,i
そして、これがフォーマットとインデントにどのように影響するかについては、これを処理するための最善のアプローチは、次のステートメントを最も外側のforステートメントに揃えることです。例えば。
Input n,m,o
For i=0To n
For j=0To m
For k=0To o
?i;j;k
Next k,j,i
QBasicを、ユーザーのキーボード入力を取得するには、いくつかの方法があります:INPUT
、LINE INPUT
、INPUT$
、とINKEY$
。
INPUT
標準の多目的入力ステートメントです。プログラムは、実行中の処理を停止し、カーソルを表示し、ユーザーが入力を入力できるようにしますEnter。INPUT
数値または文字列を読み取ることができ、複数の値をコンマ区切りで読み取ることができます。文字列をプロンプトとして指定したり、デフォルトの疑問符プロンプトを使用したり、プロンプトを完全に非表示にしたりすることができます。いくつかのサンプル呼び出し:
INPUT x$,y
?
プロンプトを使用し、コンマ区切りの文字列と数字を読み取ります。INPUT"Name";n$
Name?
文字列で
プロンプトを表示して読み取ります。INPUT"x=",x
x=
(疑問符なし!構文内のコンマに注意してください)と数値を読み取ります。INPUT;"",s$
INPUT
です)。たとえば、PRINT s$
この直後にすると、画面はのようになりますUser_inputUser_input
。INPUT
はINPUT
、フィールド区切り文字としてコンマを使用するため、コンマを含む文字列を読み取れないことです。任意の(印刷可能なASCII)文字の1行を読み取るには、を使用しますLINE INPUT
。INPUT
文字列変数でなければならない変数を1つだけ受け取ることを除いて、と同じ構文オプションがあります。もう1つの違いは、LINE INPUT
デフォルトではプロンプトが表示されないことです。必要な場合は、明示的に指定する必要があります。INPUT$(n)
プロンプトやカーソルを表示せず、ユーザーがn
文字を入力するまで待機し、それらの文字を含む文字列を返します。異なりINPUT
またはLINE INPUT
、ユーザーが押す必要はありません。Enterその後、実際にEnter文字のいずれかになります(それはC-などの言語に知られているASCII文字13を、あげます\r
)。
ほとんどの場合、これはINPUT$(1)
通常、ループ内で便利です。単一のキープレスが物事をINPUT$
する対話型プログラムで良いです。残念ながら、ASCIIコードを持つキーでのみ機能します。これは、のようなものが含まEscとBackspaceではなく、矢印キー、InsertおよびDelete、その他を。
これは、1つのキーを押した場合の結果を返すという点で1にINKEY$
似INPUT$(1)
ていますが、次の点で異なります。
INKEY$
引数を取りません。INPUT$(n)
ユーザーがn
文字を入力するまで実行を停止しますが、実行を停止INKEY$
しません。ユーザーが現在キーを押している場合、INKEY$
そのキーを表す文字列を返します。そうでなければ、を返します""
。これはINKEY$
、次のキープレスを取得するために使用する場合、ビジー待機ループでラップする必要があることを意味します。2
k$=""
WHILE""=k$
k$=INKEY$
WEND
ASCII文字(エスケープ、タブ、バックスペースなどの制御文字を含む)に対応するキーの両方INPUT$
とINKEY$
ASCII文字を返します。しかしながら、INKEY$
ASCIIコードを持たない一部のキーも処理できます。これら(ヘルプファイル)については、「INKEY $は、ヌル文字(ASCII 0)とキーボードスキャンコードで構成される2バイトの文字列を返します。」
泥だらけ?以下に例を示します。INKEY$
上記のループを使用して左矢印キーのキープレスをキャプチャk$
する場合、文字列"␀K"
(K
スキャンコード75を表す)が含まれます。右矢印の場合は"␀M"
(77)です。ページダウンは"␀Q"
(81)です。F5は"␀?"
(63)です。
まだ泥だらけ?うん。それは世界で最も直感的なものではありません。ヘルプファイルにはスキャンコードの表がありますが、私はいつも小さなプログラムを書いてその結果を印刷しINKEY$
、たくさんのキーを押して正しい値が何かを見つけます。どの文字がどのキーに対応するかがわかったら、を使用RIGHT$(k$,1)
しLEN(k$)
て、発生する可能性のあるすべてのケースを区別できます。
一番下の行?INKEY$
奇妙ですが、プログラムが非ブロッキング入力を必要とする場合、または矢印キーを使用する必要がある場合に行く唯一の方法です。
1は含みませんShift、Ctrl、Alt、PrntScr、Caps Lock、および同様。それらは数えません。:^ P
2WHILE ... WEND
ここでのイディオムは、QBasicの本で学んだことです。目的のゴルフのために、しかし、ループが短くなっています。GOTO
このLOCATE
ステートメントにより、画面上の任意の場所(通常の80x40文字のスペース制限内)にカーソルを置き、その場所に何かを印刷できます。チャレンジに対するこの回答は、これを実際に示しています(また、このトピックの他の多くのヒントと組み合わされています)。
このチャレンジでは、ユーザーが押したすべてのキャラクターを16x6グリッドで出力するように求められます。これによりLOCATE
、ASCIIコードのdivおよびmodの問題になります(a
このコード内):
LOCATE a\16-1,1+2*(a MOD 16)
そして、キャラクターを印刷します:
?CHR$(a)
QBasicでは、DIM
文を使用して変数を作成し、変数に名前と型を与えるのが慣例です。ただし、これは必須ではありません。QBasicは変数名のサフィックスによって型を派生させることもできます。変数の宣言と初期化を同時に行うことはできないため、多くの場合DIM
、codegolfでスキップするのが賢明です。機能的に同一の2つのスニペット*:
DIM a AS STRING: a = "example"
a$ = "example"
*これにより、2つの異なる変数名が作成されることに注意してください。
$
文字列、!
単精度数、倍精度の変数名の末尾に追加することで、変数の型を指定できます%
。タイプが指定されていない場合、シングルが想定されます。
a$ = "Definitely a string"
b! = "Error!"
これは配列にも当てはまることに注意してください。通常、配列は次のように定義されます。
DIM a(20) AS STRING
ただし、配列もDIM
調整する必要はありません。
a$(2) = "QBasic 4 FUN!"
a$
これは、インデックス0からインデックス10までの11スロットの文字列の配列になりました。これは、QBasicに配列の0ベースおよび1ベースのインデックスを許可するオプションがあるためです。デフォルトの配列の種類はこの両方をサポートしています。
DIM
上記で作成した20スロットの配列を覚えていますか?同じ原理が減光アレイと非減光アレイの両方に適用されるため、実際には21個のスロットがあります。
IF
ステートメントIF
ステートメントはかなり高価であり、それらをゴルフでダウンすると多くのバイトを節約できます。
次のことを考慮してください(Erik the Outgolferの回答から引用):
IF RND<.5THEN
x=x-1
a(i)=1
ELSE
y=y-1
a(i)=0
ENDIF
最初にできることはENDIF
、1行のIF
ステートメントを使用して保存することです。
IF RND<.5THEN x=x-1:a(i)=1ELSE y=y-1:a(i)=0
これは、他の行と同じ行に配置しない限り機能します。特に、ネストしている場合IF
ステートメントがある場合、最も内側のステートメントのみを1行にすることができます。
ただし、この場合、IF
数学を使用して完全に排除できます。私たちが実際に欲しいものを考えてみましょう:
RND<.5
がtrue(-1
)の場合、以下が必要です。
x
1減少するy
同じままにするa(i)
1になるRND<.5
がfalse(0
)の場合、以下が必要です。
x
同じままにするy
1減少するa(i)
0になる今、私たちは、変数に(条件付きの結果を保存した場合r=RND<.5
)を、私たちは、新しい値を計算することができx
、y
およびa(i)
:
r
で-1
、x=x-1
。いつr
ですか0
、x=x+0
。r
で-1
、y=y+0
。いつr
ですか0
、y=y-1
。r
で-1
、a(i)=1
。ときr
です0
、a(i)=0
。したがって、最終的なコードは次のようになります。
r=RND<.5
x=x+r
y=y-1-r
a(i)=-r
元のバージョンに比べてなんと20バイト(40%)節約できます。
数学的なアプローチは驚くほど頻繁に適用できますが、2つのケースでロジックに違いがある場合(たとえば、あるケースでは何かを入力し、別のケースでは入力しない場合)、を使用する必要がありますIF
。
QBasicの配列は、DIM
11個のスロットしか持たずにインスタンス化された場合。チャレンジで11スロット(またはNスロット、Nは11より大きい)が必要な場合は、DIM
、アレイを使用あり。また、この配列にデータを入力したいと仮定します。
DIM a$(12)
a$(0) = "Value 1"
a$(1) = "Value 2"
...
ゴルフでさえ、これは多くのスペースを占める可能性があります。このような場合、これを行うにはバイト単位で安くなる場合があります。
a$ = "value 1value 2"
ここでは、すべてを1つの連結文字列に配置します。後で、次のようにアクセスします。
?MID$(a$,i*7,7)
このアプローチでは、すべての値の長さが等しいことが重要です。最も長い値を取り、他のすべてを埋めます:
a$="one two threefour "
最後の値を埋め込む必要はありません。また、引用符をスキップすることもできます!チャレンジで回答に空白を使用できないことが指定されている場合は、RTRIM$()
を修正するします。
PRINT
(?
)いくつかの癖があります数字は、先頭と末尾のスペースで印刷されます。
印刷すると改行が追加されます。この動作は、文の最後にコンマを追加してタブを挿入するか、セミコロンを追加して挿入を回避することで変更できます。
使用する必要はありません&
か、;
例えば、印刷する際に明確な操作の間。?1"x"s$
数字1
、各辺にスペース、文字x
、s$
?"foo"
?"bar"
?10
?"foo",
?"bar"
?"foo";
?"bar"
?1CHR$(65)
?1" "CHR$(65)
?"A","B
出力
foo
bar
10
foo bar
foobar
1 A
1 A
A B
改行を印刷するには、 ?
-
がそこに印刷されます。数字の後にスペースも印刷されます。これらのスペースを取り除くために私が発見した最良の方法はPRINT USING
、この回答に追加したい場合、または別の回答にする必要がある場合に--dunnoです。
WRITE
の代わりに役立つかもしれません PRINT
PRINT
これは非常に柔軟で?
ショートカットがあるため、通常は出力を行う方法です。ただし、WRITE
コマンドは特定の状況でバイトを節約できます。
WRITE
、二重引用符("
)で囲みます。二重引用符付きの出力が必要な場合は、WRITE s$
よりもはるかに短くなり?CHR$(34);s$;CHR$(34)
ます。たとえば、最短の既知のQBasic quineを参照してください。WRITE
前後にスペースを追加しませんPRINT
。WRITE n
は、よりもはるかに短いです?MID$(STR$(n),2)
。たとえば、QB64のFizzBuzzを参照してください。WRITE
それらをコンマで区切ります:WRITE 123,"abc"
outputs 123,"abc"
。これが役立つシナリオは考えられませんが、それが存在しないという意味ではありません。の制限WRITE
:
PRINT a;b
。LOCATE
、それには多くのバイトがかかります。)文字列ではなく文字で機能する関数がいくつかありますがchar
、QBasicにはデータ型はなく、型のみがありstring ($)
ます。たとえば、ASC()
文字のASCIIキーコードを返す関数を取り上げます。入ったら
PRINT ASC("lala")
最初だけ l
QBasicによって考慮されます。このように、文字列を1の長さに切り詰める必要はありません。
別の例は、質問がSTRING$()
回答の1つで使用されているこの質問から来ています。
STRING $関数は、数値nと文字列s $の2つの引数を取り、s $の最初の文字のn個のコピーで構成される文字列を作成します
@DLosc、ここ
QBasicは、複数文字の文字列を提供し、1文字のみを必要とする場合、最初の文字を自動的に取得し、残りを無視することに注意してください。