QBasicでのゴルフのヒント


13

QBasicでゴルフをするための一般的なヒントは何ですか?私は、少なくともQBasicに特有のゴルフ問題全般のコーディングに適用できるアイデアを探しています(たとえば、「コメントの削除」は答えではありません)。

QB64エミュレーターに関するヒントも歓迎します。Microsoft QBasicにはない追加機能がいくつかあります。


あなたのやる気に興味があります。私は10年生のプログラミングクラス以来、QBASICを使用していません。バージョン管理なしで1.44フロッピーディスクに直接保存し、(通常)致命的な障害を回避したのは驚くべきことです。
アンドリューブルーザ

5
@AndrewBrēzaモチベーション?あらゆる言語でゴルフをするという私のモチベーションと同じ:楽しみのために!私はQBasicで小さなプログラムを書くことを楽しんでいます(ただし、深刻なものには使いたくありません)。また、サウンドとグラフィックス(テキストとピクセルの両方)が組み込まれているという追加のボーナスがありますが、私の好みの「本物の」言語であるPythonはそうではありません。
DLosc

pythonよりもQBasicでグラフィカルゲームを作成する方がはるかに簡単です。
アヌーシュ

ブラウザでグラフィカルなQBasicアプリケーションを直接試してみたい人は、これを使用できます:github.com/nfriend/origins-host
mbomb007

回答:


10

ループ構造を知る

QBasicはは、いくつかのループ構造を持っていますFOR ... NEXTWHILE ... WENDDO ... LOOPGOTOまたは(場合によっては)RUNループすることもできます。

  • FOR ... NEXTそれが何をするかでかなり良いです。Pythonとは異なり、ほとんどの場合、同等のWHILEor 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 ... WEND0回実行する必要があるループがある場合に適しています。ただし、ループが少なくとも1回実行されることがわかっている場合は、GOTO1バイト短くなる可能性があります。

    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桁の行番号を使用します。ステートメントGOTOTHEN一部にa のみが含まれている場合、IF2つの等しく簡潔なショートカット構文が使用できることに注意してください。

    IF x>y GOTO 1
    IF x>y THEN 1
    

    GOTOまた、より複雑な制御フローを作成するために使用できます。否定論者はこれを「スパゲッティコード」と呼んでいますが、これはコードゴルフです。読み難さはほとんど美徳です。GOTO誇り!

  • RUNプログラム内の特定の場所にジャンプする必要があり、変数の値を保持する必要がない場合に便利です。RUN単独でプログラムを上部から再起動します。ラベルまたは行番号を使用すると、その行から再開されます。主にステートレスの無限ループ作成するために使用しました。

5

ショートカットを使用するPRINTと、REM

?代わりにPRINT、および(コメント)の'代わりに使用できますREM

''charまたはstring構文の一部としてサポートする言語でポリグロットを作成するときにも役立つかもしれません。


5

可分性テスト

ある整数が別の整数で割り切れるかどうかをテストする必要があるプログラムでは、明白な方法は以下を使用することMODです:

x MOD 3=0

しかし、より短い方法は整数除算を使用することです:

x\3=x/3

つまり、xint-div 3xfloat-div と等しくなり3ます。

これらのアプローチはどちらも0偽りと真実を返すため-1、結果を否定するか、加算する代わりに減算する必要があることに注意してください。


逆の条件が必要な場合(つまりxで割り切れない場合3)、明らかなアプローチは不等号演算子を使用することです:

x\3<>x/3

しかし、x負でないことが保証されている場合は、バイトを保存できます。整数除算では結果が切り捨てられるため、常に浮動小数点除算以下になります。したがって、条件は次のように記述できます。

x\3<x/3

同様に、x負であることが保証されている場合、切り捨てにより結果が増加するため、と書くことができますx\3>x/3。の兆候がわからない場合xは、固執する必要があります<>


5

スキャナーの悪用

多くの言語と同様に、削除できる文字と削除できない文字を知ることが重要です。

  • シンボルの隣のスペースはすべて削除できます。 IF""=a$THEN?0
  • 通常、数字とその順序で起こる手紙の間でスペースを取り除くことができます:FOR i=1TO 10STEP 2。QBasic 1.1(archive.orgで入手可能)とQB64にはいくつかの違いがあります。
    • QBasic 1.1では、数字とそれに続く文字の間のスペースを削除できます。さらに、print文では、連続した値の間にセミコロンを推測します:?123xなりPRINT 123; x。上記の例外は、1e2やなどのシーケンスです1d+3。これらは科学表記法として扱われ、100!そして1000#(それぞれ、シングルおよび倍精度)。
    • QB64は、一般的に同じであるが、数字が続くことができないdeまたはfそれらは、十分に形成された科学的表記リテラルの一部である場合を除き、すべてで。(たとえば、行番号の後にスペースを省略することができない1 FORか、9 ENDあなたが適切なQBasicをでできるように、。)print文でこれだけ推論のセミコロン表現の一つは、文字列の場合:?123"abc"作品ではなく、?TAB(5)123?123x
  • セミコロンと言えば、QBasic 1.1は末尾のセミコロンPRINTTABorの呼び出しで終わるステートメントに追加しますSPC。(QB64はサポートしていません。)
  • 0小数点(.1または1.)の前後で省略できますが、両方(.)はできません。
  • ENDIFはと同等END IFです。
  • 文字列の最後の二重引用符は、行末で省略できます。

endif実際にQB64で動作します。この回答を
-wastl

@wastlそうです。QB64で最初にテストしたとき、構文エラーだった古いバージョンを使用していました。言及してくれてありがとう!
DLosc

4

Nextステートメントを組み合わせる

Next:Next:Next

まで凝縮される可能性があります

Next k,j,i

どこのためのイテレータForループがありijk-の順で。

たとえば、以下(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

4

入力方法を知る

QBasicを、ユーザーのキーボード入力を取得するには、いくつかの方法があります:INPUTLINE INPUTINPUT$、とINKEY$

  • INPUT標準の多目的入力ステートメントです。プログラムは、実行中の処理を停止し、カーソルを表示し、ユーザーが入力を入力できるようにしますEnterINPUT数値または文字列を読み取ることができ、複数の値をコンマ区切りで読み取ることができます。文字列をプロンプトとして指定したり、デフォルトの疑問符プロンプトを使用したり、プロンプトを完全に非表示にしたりすることができます。いくつかのサンプル呼び出し:
    • INPUT x$,y
      デフォルトの? プロンプトを使用し、コンマ区切りの文字列と数字を読み取ります。
    • INPUT"Name";n$
      Name? 文字列で プロンプトを表示して読み取ります。
    • INPUT"x=",x
      プロンプトx=(疑問符なし!構文内のコンマに注意してください)と数値を読み取ります。
    • INPUT;"",s$
      プロンプトを抑制し(上記のコンマ構文と空のプロンプト文字列を使用)、文字列を読み取り、ユーザーがEnterキーを押しても次の行に移動しません(その後のセミコロンINPUTです)。たとえば、PRINT s$この直後にすると、画面はのようになりますUser_inputUser_input
  • 欠点の1つINPUTINPUT、フィールド区切り文字としてコンマを使用するため、コンマを含む文字列を読み取れないことです。任意の(印刷可能なASCII)文字の1行を読み取るには、を使用しますLINE INPUTINPUT文字列変数でなければならない変数を1つだけ受け取ることを除いて、と同じ構文オプションがあります。もう1つの違いは、LINE INPUTデフォルトではプロンプトが表示されないことです。必要な場合は、明示的に指定する必要があります。
  • INPUT$(n)プロンプトやカーソルを表示せず、ユーザーがn文字を入力するまで待機し、それらの文字を含む文字列を返します。異なりINPUTまたはLINE INPUT、ユーザーが押す必要はありません。Enterその後、実際にEnter文字のいずれかになります(それはC-などの言語に知られているASCII文字13を、あげます\r)。

    ほとんどの場合、これはINPUT$(1)通常、ループ内で便利です。単一のキープレスが物事をINPUT$する対話型プログラムで良いです。残念ながら、ASCIIコードを持つキーでのみ機能します。これは、のようなものが含まEscBackspaceではなく、矢印キー、InsertおよびDelete、その他を。

  • これは、1つのキーを押した場合の結果を返すという点で1INKEY$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は含みませんShiftCtrlAltPrntScrCaps Lock、および同様。それらは数えません。:^ P

2WHILE ... WENDここでのイディオムは、QBasicの本で学んだことです。目的のゴルフのために、しかし、ループが短くなっていますGOTO


3

LOCATEは本当に強力です

このLOCATEステートメントにより、画面上の任意の場所(通常の80x40文字のスペース制限内)にカーソルを置き、その場所に何かを印刷できます。チャレンジに対するこの回答は、これを実際に示しています(また、このトピックの他の多くのヒントと組み合わされています)。

このチャレンジでは、ユーザーが押したすべてのキャラクターを16x6グリッドで出力するように求められます。これによりLOCATE、ASCIIコードのdivおよびmodの問題になります(aこのコード内):

LOCATE a\16-1,1+2*(a MOD 16)

そして、キャラクターを印刷します:

?CHR$(a)

3

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個のスロットがあります。


これが配列にも当てはまることに気づきませんでした。面白い。
-trichoplax

3

短縮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)を、私たちは、新しい値を計算することができxyおよびa(i)

  • ときr-1x=x-1。いつrですか0x=x+0
  • ときr-1y=y+0。いつrですか0y=y-1
  • ときr-1a(i)=1。ときrです0a(i)=0

したがって、最終的なコードは次のようになります。

r=RND<.5
x=x+r
y=y-1-r
a(i)=-r

元のバージョンに比べてなんと20バイト(40%)節約できます。


数学的なアプローチは驚くほど頻繁に適用できますが、2つのケースでロジックに違いがある場合(たとえば、あるケースでは何かを入力し、別のケースでは入力しない場合)、を使用する必要がありますIF


3

時には、配列を避けるべきです

QBasicの配列は、DIM11個のスロットしか持たずにインスタンス化された場合。チャレンジで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$()を修正するします。

ここで、この手法の動作を確認できます


3

PRINT?)いくつかの癖があります

数字は、先頭と末尾のスペースで印刷されます。

印刷すると改行が追加されます。この動作は、文の最後にコンマを追加してタブを挿入するか、セミコロンを追加して挿入を回避することで変更できます。

使用する必要はありません&か、;例えば、印刷する際に明確な操作の間。?1"x"s$数字1、各辺にスペース、文字xs$

?"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です。
DLosc

2

WRITE の代わりに役立つかもしれません PRINT

PRINTこれは非常に柔軟で?ショートカットがあるため、通常は出力を行う方法です。ただし、WRITEコマンドは特定の状況でバイトを節約できます。

  • 文字列を出力するときWRITE、二重引用符(")で囲みます。二重引用符付きの出力が必要な場合は、WRITE s$よりもはるかに短くなり?CHR$(34);s$;CHR$(34)ます。たとえば、最短の既知のQBasic quineを参照してください。
  • 数値を出力するときに、WRITE前後にスペースを追加しませんPRINTWRITE nは、よりもはるかに短いです?MID$(STR$(n),2)。たとえば、QB64のFizzBu​​zzを参照してください。
  • 複数の値を出力するときは、WRITEそれらをコンマで区切ります:WRITE 123,"abc"outputs 123,"abc"。これが役立つシナリオは考えられませんが、それが存在しないという意味ではありません。

の制限WRITE

  • withのようなセパレータなしで複数の値を出力する方法はありませんPRINT a;b
  • 出力の最後で改行を抑制する方法はありません。(でこれを回避できるかもしれませんがLOCATE、それには多くのバイトがかかります。)

1

QBasicは、関数への入力をマングルする場合があります。それを虐待する!

文字列ではなく文字で機能する関数がいくつかありますがchar、QBasicにはデータ型はなく、型のみがありstring ($)ます。たとえば、ASC()文字のASCIIキーコードを返す関数を取り上げます。入ったら

PRINT ASC("lala")

最初だけ l QBasicによって考慮されます。このように、文字列を1の長さに切り詰める必要はありません。

別の例は、質問STRING$()回答の1つで使用されているこの質問から来ています。

STRING $関数は、数値nと文字列s $の2つの引数を取り、s $の最初の文字のn個のコピーで構成される文字列を作成します

@DLosc、ここ

QBasicは、複数文字の文字列を提供し、1文字のみを必要とする場合、最初の文字を自動的に取得し、残りを無視することに注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.