QBasic'te golf için ipuçları


13

QBasic'te golf oynamak için hangi genel ipuçlarınız var? Ben genel olarak en azından biraz QBasic özgü (örneğin "yorumları kaldırmak" bir cevap değildir) kod golf sorunları uygulanabilir uygulanabilir fikirler arıyorum.

QB64 öykünücüsü ile ilgili ipuçları da kabul edilir. Microsoft QBasic'te olmayan bazı ekstra özelliklere sahiptir.


Motivasyonunu merak ediyorum. 10. sınıf programlama sınıfımdan beri QBASIC kullanmadım. Herhangi bir sürüm kontrolü olmadan doğrudan 1.44 diskete nasıl kaydettiğim ve (genellikle) felaket arızalarından kaçındım.
Andrew Brēza

5
@ AndrewBrēza Motivasyon? Herhangi bir dilde golf oynama motivasyonumla aynı: eğlence için! QBasic'te küçük programlar yazmaktan hoşlanıyorum (ciddi bir şey için kullanmak istemem). Ayrıca, tercih ettiğim "gerçek" dilim olan Python'un içermediği ses ve grafik (hem metin hem de piksel) var.
DLosc

QBasic'te grafik oyunları yazmak python'dan çok daha kolaydır.
Anush

Herkes grafiksel QBasic uygulamalarını doğrudan tarayıcıda denemek isterse bunu kullanabilirler: github.com/nfriend/origins-host
mbomb007 19:18

Yanıtlar:


10

Döngü yapılarınızı bilin

QBasic birkaç döngü yapıları vardır: FOR ... NEXT, WHILE ... WEND, ve DO ... LOOP. Döngü için GOTOveya öğesini (bazı durumlarda) da kullanabilirsiniz RUN.

  • FOR ... NEXTyaptığı işte oldukça iyidir. Python'dan farklı olarak, küçük bir meraklı olsa bile neredeyse her zaman eşdeğerden WHILEveya GOTOdöngüden daha kısadır :

    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
    

    Sonra değişken adını tekrarlamanız gerekmediğini NEXTve sayılar ile aşağıdaki anahtar kelimelerin çoğu arasındaki boşluğu kaldırabileceğinizi unutmayın.

  • WHILE ... WEND0 kez yürütülmesi gerekebilecek bir döngünüz olduğunda kullanışlıdır. Ancak, döngünün en az bir kez yürütüleceğini biliyorsanız, GOTObir bayt daha kısa olabilir:

    WHILE n>1:n=n\2:WEND
    1n=n\2:IF n>1GOTO 1
    
  • Sadece DO ... LOOPsonsuz döngüler için kullanıyorum ( RUNbunun yerine nerede kullanılabileceği hariç ). Koşulsuz olarak aynı sayıda karaktere mal olmakla birlikte GOTO, okumak biraz daha sezgiseldir. ("Sonsuz döngü" ifadesinin, a'yı kullanmaktan çıkardığınız döngüler içerebileceğini unutmayın GOTO.) DO WHILE/ DO UNTIL/ LOOP WHILE/ LOOP UNTILSözdizimi çok ayrıntılı; WHILEveya GOTOuygun şekilde kullanmanız daha iyi olur .
  • GOTOyukarıda belirtildiği gibi, bir do / while döngüsü yazmanın en kısa genel yoludur. Etiketler yerine tek haneli satır numaralarını kullanın. A ifadenin bir bölümünde GOTOtek şey olduğunda, iki eşit kısayol kısayol sözdizimi olduğunu unutmayın:THENIF

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

    GOTOdaha karmaşık kontrol akışları oluşturmak için de kullanılabilir . Muhalifler buna "spagetti kodu" derler, ancak bu kod golfüdür: okunamazlık neredeyse bir erdemdir! GOTOgurur!

  • RUNprogramda sabit bir yere atlamanız gerektiğinde ve değişkenlerin değerlerini korumanız gerekmediğinde kullanışlıdır. RUNtek başına programı üstten başlatacak; bir etiket veya satır numarasıyla, bu satırda yeniden başlatılır. Ben daha çok vatansız sonsuz döngüler oluşturmak için kullandım .

5

PRINTVe için kısayollar kullanınREM

Sen kullanabilirsiniz ?yerine PRINTve 'yerine REM(yorum).

''char veya string söz diziminin bir parçası olarak desteklenen dillerle çok dilde yazarken de yararlı olabilir .


5

Bölünebilirlik testi

Bir tamsayının diğerine bölünebilir olup olmadığını test etmenizi gerektiren programlarda, bariz yol şu şekildedir MOD:

x MOD 3=0

Ancak daha kısa bir yol, tamsayı bölümünü kullanmaktır:

x\3=x/3

Yani xint-div 3, xfloat-div değerine eşittir 3.

Her iki yaklaşımın da falsey 0ve -1doğruluk için geri döneceğini unutmayın, bu nedenle sonucu reddetmek veya eklemek yerine çıkarmak zorunda kalabilirsiniz.


Eğer tersi durumunu gerekiyorsa (yani xolduğu değil bölünebilen 3), bariz bir yaklaşım operatörü Eşit değildir kullanmaktır:

x\3<>x/3

Ancak x, negatif olmadığı garanti edilirse , bir bayt kaydedebiliriz. Tamsayı bölümü sonucu kısaltır, bu nedenle her zaman kayan bölümden küçük veya ona eşit olacaktır. Bu nedenle, durumu şu şekilde yazabiliriz:

x\3<x/3

Benzer şekilde, xnegatif olduğu garanti edilirse , kesilme sonucu artırır ve yazabiliriz x\3>x/3. Eğer işaretini bilmiyorsanız, bağlı xkalmanız gerekir <>.


5

Tarayıcı kötüye kullanımı

Birçok dilde olduğu gibi, hangi karakterlerin kaldırılıp kaldırılamayacağını bilmek önemlidir.

  • Bir sembolün yanındaki herhangi bir boşluk kaldırılabilir: IF""=a$THEN?0
  • Uzay genellikle bir rakam ve ortaya çıkan bir mektup arasına çıkarılabilir bu sırayla : FOR i=1TO 10STEP 2. QBasic 1.1 ( archive.org adresinde mevcuttur ) ve QB64 arasında bazı farklılıklar vardır :
    • QBasic 1.1 herhangi bir rakam ve bir sonraki harf arasındaki boşluğun kaldırılmasına izin verir. Ayrıca, basılı ifadelerde, ardışık değerler arasında noktalı virgül çıkarır: ?123xolur PRINT 123; x. Yukarıda istisnaları gibi dizileridir 1e2ve 1d+3bilimsel gösterim olarak kabul edilir ve genişletilir, 100!ve 1000#(sırasıyla, tek ve çift-hassas).
    • Qb64 genellikle aynıdır, ancak basamak takip edilemez d, eya da fbir çok iyi oluşturulmuş bilimsel gösterim sabitin bir parçası olmadığı sürece hiç. (Örneğin, satır numarasından sonra boşluk geçemeyeceğim 1 FORveya 9 ENDuygun QBasic içinde olabildiğince gibi.) Baskı tablolara Sadece infers noktalı virgül ifadelerden birinin bir dize ise: ?123"abc"eserler değil, ?TAB(5)123ya da ?123x.
  • Noktalı virgüllerden bahsetmişken, QBasic 1.1 veya PRINTçağrısıyla biten bir ifadeye bir noktalı virgül ekler . (QB64 bunu yapmaz.)TABSPC
  • 0ondalık noktadan ( .1veya 1.) önce veya sonra atlanabilir , ancak her ikisi de ( .) kullanılamaz.
  • ENDIFeşittir END IF.
  • Bir dizenin kapanış çift tırnak çizgisinin sonunda atlanabilir.

endifAslında qb64 çalışır, bkz bu cevabı
Wastl

@wastl Öyle. QB64'te ilk test ettiğimde, sözdizimi hatası olduğu daha eski bir sürüm kullanıyordum. Bahsettiğiniz için teşekkürler!
DLosc

4

Komut Nextİfadeleri

Next:Next:Next

Yoğunlaştırılabilir

Next k,j,i

için yineleyiciler nerede Fordöngüler vardır i, jve k- bu sırayla.

Örneğin aşağıdaki (69 Bayt)

Input n,m,o
For i=0To n
For j=0To m
For k=0To o
?i;j;k
Next
Next
Next

65 bayta kadar yoğunlaştırılabilir

Input n,m,o
For i=0To n
For j=0To m
For k=0To o
?i;j;k
Next k,j,i

Ve bunun biçimlendirme ve girintiyi nasıl etkilediği kadarıyla, bunu ele almak için en iyi yaklaşımın bir sonraki ifadeyi ifade için en dışa hizalamak olduğunu düşünüyorum. Örneğin.

Input n,m,o
For i=0To n
    For j=0To m
        For k=0To o
            ?i;j;k
Next k,j,i

4

Giriş yöntemlerinizi bilin

QBasic kullanıcı klavye girişi almak için çeşitli yollar vardır: INPUT, LINE INPUT, INPUT$, ve INKEY$.

  • INPUTstandart çok amaçlı giriş ifadenizdir. Program yaptığı işi durdurur, bir imleç görüntüler ve kullanıcının tarafından sonlandırılan bir girdi yazmasına izin verir Enter. INPUTsayıları veya dizeleri okuyabilir ve virgülle ayrılmış birden çok değeri okuyabilir. Bir dize istemi olarak belirtebilirsiniz, varsayılan soru işareti istemi ile gidebilirsiniz ve hatta (bu gece öğrendim) istemi tamamen bastırabilirsiniz. Bazı örnek çağrılar:
    • INPUT x$,y
      Varsayılan ? istemi kullanır ve virgülle ayrılmış bir dize ve bir sayı okur.
    • INPUT"Name";n$
      Name? Bir dize ister ve okur.
    • INPUT"x=",x
      İle x=sorulur (soru işareti yok! Sözdiziminde virgül not edin) ve bir sayı okur.
    • INPUT;"",s$
      İstemi bastırır (boş bir bilgi istemi dizesi ile yukarıdaki virgül sözdizimini kullanarak), bir dize okur ve kullanıcı girdiğinde bir sonraki satıra geçmez (noktalı virgül sonradan INPUTbunu yapar). Örneğin, PRINT s$bundan hemen sonra, ekranınız gibi görünecektir User_inputUser_input.
  • Bunun bir dezavantajı, alan ayırıcısı olarak virgül kullandığından INPUT, içinde virgül bulunan bir dize okuyamamanızdır INPUT. Tek bir rasgele (yazdırılabilir ASCII) karakter okumak için kullanın LINE INPUT. INPUTBir dize değişkeni olması gereken tam olarak bir değişkeni alması dışında , aynı sözdizimi seçeneklerine sahiptir. Diğer fark, LINE INPUTvarsayılan olarak bir istem görüntülememesidir; İsterseniz, bunu açıkça belirtmeniz gerekir.
  • INPUT$(n)hiçbir istem veya imleç görüntülemez, ancak kullanıcı nkarakter girene kadar bekler ve sonra bu karakterleri içeren bir dize döndürür. Aksine INPUTveya LINE INPUTkullanıcı basına ihtiyacı yoktur Entersonradan ve aslında Enterkarakterlerden biri olabilir (o bilinen ASCII karakter 13, vereceğiz C benzeri diller olarak \r).

    Çoğu zaman, bu INPUT$(1)genellikle bir döngüde olduğu gibi yararlıdır . tek tuş basımlarının bir şeyler yaptığı etkileşimli programlardaINPUT$ iyidir . Ne yazık ki, yalnızca ASCII kodları olan anahtarlarla çalışır; buna ve gibi ok tuşları ve ve diğerleri dahil değildir.EscBackspaceInsertDelete

  • Bu da devreye INKEY$giriyor. INPUT$(1)Tek bir tuşa basmanın 1 sonuçlarını döndürmesine benziyor , ancak farklı:

    • INKEY$ hiçbir argüman almaz.
    • İken INPUT$(n)kullanıcı dek durur yürütme girdiği nkarakterleri, INKEY$yürütme durdurmak etmez. Kullanıcı şu anda bir tuşa INKEY$basıyorsa, o tuşu temsil eden bir dize döndürür; değilse, geri döner "". Bu INKEY$, bir sonraki tuşa basmak için kullanmak istiyorsanız, onu meşgul bekleyen bir döngüye sarmanız gerektiği anlamına gelir : 2

      k$=""
      WHILE""=k$
      k$=INKEY$
      WEND
      
    • Hem INPUT$ve INKEY$anahtarlar için dönüş ASCII karakterleri ki (kaçış, sekme ve backspace gibi kontrol karakterleri dahil) ASCII karakter karşılık gelir. Ancak, INKEY$ASCII kodları olmayan bazı tuşları da işleyebilir. Bunlar için (yardım dosyası ile ilgili), "INKEY $, boş karakterden (ASCII 0) ve klavye tarama kodundan oluşan 2 baytlık bir dize döndürür."

      Çamur kadar temiz? İşte bazı örnekler. INKEY$Sol ok tuşuna bir kez basmak için yukarıdaki döngüyü kullanırsanız, k$dizeyi içerir "␀K"( Ktemsil eden tarama koduyla 75). Sağ ok için "␀M"(77). Sayfa aşağı "␀Q"(81). F5, "␀?"(63) 'tür.

      Hala çamur gibi açık mı? Evet. Dünyadaki en sezgisel şey değil. Yardım dosyasının tarama kodları tablosu vardır, ancak her zaman sadece sonuçlarını yazdırmak için küçük bir program yazarım INKEY$ve doğru değerlerin ne olduğunu bulmak için bir tuşa basın. Hangi tuşlara karşılık hangi karakterler öğrendiğinizde, kullanabilir RIGHT$(k$,1)ve LEN(k$)karşılaşabileceğiniz tüm farklı durumlarda arasında ayrım.

    Sonuç olarak? INKEY$tuhaftır, ancak programınız engellemeyen giriş gerektiriyorsa veya ok tuşlarını kullanmanız gerekiyorsa gitmenin tek yolu budur .


1 dahil değil Shift, Ctrl, Alt, PrntScr, Caps Lock, ve benzeri. Bunlar sayılmaz. : ^ P

2 Buradaki WHILE ... WENDdeyim QBasic kitaplarımda öğrendiklerim. Ancak golf amaçlı bir GOTOdöngü daha kısadır .


3

LOCATE gerçekten güçlü olabilir

LOCATEİfadende (olağan 80x40 karakter boşluk sınırlar içinde) ekranda imleç hiçbir yerinde yerleştirmek ve bu konumdaki bir şey yazdırmak için izin verir. Bu soruna verilen cevap gerçekten bunu gösteriyor (ve aynı zamanda bu konuyla ilgili diğer birçok ipucuyla birleştiriliyor).

Zorluk, bir kullanıcının 16x6 ızgarasında bastığı her karakteri çıkarmamızı ister. İle LOCATEbu (ASCII kodu üzerinde div ve mod meselesi basitçe abu kodda):

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

Ve sonra karakteri yazdırın:

?CHR$(a)

3

QBasic'de, DIMdeğişkenleri oluşturmak ve onlara bir ad ve tür vermek için deyimi kullanmak gelenekseldir . Ancak, bu zorunlu değildir, QBasic değişken adının sonekine göre bir tür de türetebilir. Bir değişkeni aynı anda bildirip başlatamadığınız için, DIMin codegolf'u atlamak genellikle akıllıca olur . İşlevsel olarak aynı olan iki parçacık *:

DIM a AS STRING: a = "example"
a$ = "example"

* Bunun iki farklı değişken adı oluşturduğunu unutmayın.

Değişkenlerin türünü, $dizeler, !tek kesinlikli sayılar ve %çiftler için değişken adının sonuna ekleyerek belirtebiliriz . Hiçbir tür belirtilmediğinde bekarlar varsayılır.

a$ = "Definitely a string"
b! = "Error!"

Bunun diziler için de geçerli olduğunu unutmayın. Genellikle bir dizi şu şekilde tanımlanır:

DIM a(20) AS STRING

Ancak dizilerin de orta olması gerekmez DIM:

a$(2) = "QBasic 4 FUN!"

a$artık 11 yuvalı dizeler için bir dizidir : indeks 0'dan indeks 10'a dahil. Varsayılan dizi türü bu yolu destekler.

DIMYukarıda gördüğümüz yirmi yuvalı diziyi hatırlıyor musunuz? Aslında 21 yuva vardır, çünkü aynı prensip hem soluk hem de soluk olmayan diziler için geçerlidir.


Bunun dizilere de uygulandığını hiç fark etmedim. İlginç.
trichoplax

3

kısaltılması IFifadeleri

IF ifadeler oldukça pahalıdır ve bunları golf oynamak çok fazla bayt tasarrufu sağlayabilir.

Aşağıdakileri düşünün ( Outgolfer Erik'in cevabından uyarlanmıştır ):

IF RND<.5THEN
x=x-1
a(i)=1
ELSE
y=y-1
a(i)=0
ENDIF

Yapabileceğimiz ilk şey, ENDIFtek satırlık bir IFifade kullanarak kaydetmek :

IF RND<.5THEN x=x-1:a(i)=1ELSE y=y-1:a(i)=0

Bu, başka bir şeyle aynı satıra koymaya çalışmadığınız sürece çalışır. Özellikle, iç içe IFifadeleriniz varsa, yalnızca en içteki ifade tek satırlı olabilir.

Ancak bu durumda, IFmatematiği kullanarak tamamen ortadan kaldırabiliriz . Aslında ne istediğimizi düşünün:

  • Eğer RND<.5(doğrudur -1), biz istiyorum:
    • x 1 azaltmak
    • y aynı kalmak
    • a(i) 1 olmak
  • Aksi takdirde, RND<.5false ( 0) ise:
    • x aynı kalmak
    • y 1 azaltmak
    • a(i) 0 olmak

Biz (bir değişkenin koşullu sonucu kaydetmek Şimdi eğer r=RND<.5), biz yeni değerler hesaplayabilir x, yve a(i):

  • Ne rolduğunu -1, x=x-1; ne zaman rolduğunu 0, x=x+0.
  • Ne rolduğunu -1, y=y+0; ne zaman rolduğunu 0, y=y-1.
  • Ne rolduğunu -1, a(i)=1; ne zaman rolduğunu 0, a(i)=0.

Son kodumuz şöyle:

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

orijinal sürümün üzerine 20 bayt (% 40) tasarruf sağladı.


Matematik yaklaşımı şaşırtıcı bir şekilde sık sık uygulanabilir, ancak iki durum arasında mantıkta bir fark olduğunda (örneğin bir durumda bir şey girmeniz gerektiğinde, diğerinde değil), yine de kullanmanız gerekecektir IF.


3

Bazen dizilerden kaçınmalısınız

QBasic'deki diziler, DIMyalnızca 11 yuvaya sahip olmadan başlatıldığında . Bir meydan okuma 11'den fazla yuva (veya N'nin 11'den büyük olabileceği N yuvası) gerektiriyorsa DIM, diziyi kullanmalısınız. Ayrıca, bu diziyi verilerle doldurmak istediğimizi varsayalım:

DIM a$(12)
a$(0) = "Value 1"
a$(1) = "Value 2"
...

Golf bile olsa, bu çok yer kaplayabilir. Bu gibi durumlarda, bayt olarak bunu yapmak daha ucuz olabilir:

a$ = "value 1value 2"

Burada, her şeyi 1 sıralı dizeye yerleştiriyoruz. Daha sonra buna şu şekilde erişiyoruz:

?MID$(a$,i*7,7)

Bu yaklaşım için, tüm değerlerin eşit uzunlukta olması önemlidir. En uzun değeri alın ve diğerlerini doldurun:

a$="one  two  threefour "

Son değeri doldurmanıza gerek yoktur ve kapanış tekliflerini bile atlayabilirsiniz! Zorluk, yanıtta boşluk RTRIM$()bırakılmasına izin verilmiyorsa, bunu düzeltmek için kullanın .

Bu tekniği burada çalışırken görebilirsiniz .


3

PRINT( ?) bazı tuhaflıkları var

Sayılar ön ve arka boşluklarla yazdırılır.

Yazdırma satır sonu ekler. Bu davranış, ifadenin sonuna sekme eklemek için virgül veya herhangi bir eklemeden kaçınmak için noktalı virgül eklenerek değiştirilebilir:

Yazdırma sırasında farklı işlemler arasında &veya kullanılması gerekmez ;, ör. ?1"x"s$numarayı 1, her iki yanında boşluklar, harf xve içeriks$

?"foo"
?"bar"
?10
?"foo",
?"bar"
?"foo"; 
?"bar"
?1CHR$(65)
?1" "CHR$(65)
?"A","B

çıktılar

foo
bar
 10
foo           bar
foobar
 1 A
 1  A
A             B

Bir çizgiyi yazdırmak sadece ?


Özellikle sayıları yazdırırken: negatif olmayan sayılardan önce bir boşluk basılır; aksi takdirde, -orada bir eksi işareti basılır. Sayıdan sonra bir boşluk da yazdırılır. Bu alanlardan kurtulmak için keşfettiğim en iyi yol PRINT USING, bunu bu cevaba eklemek veya ayrı bir cevap olması gerekiyorsa --dunno.
DLosc

2

WRITE yerine yararlı olabilir PRINT

PRINTgenellikle oldukça esnek ve ?kısayola sahip olduğu için çıktı almak istediğiniz yoldur . Ancak, WRITEkomut belirli durumlarda baytlarınızı kaydedebilir:

  • Bir dize çıkarırken, dizeyi WRITEçift ​​tırnak ( ") içine alır. Çift tırnak ile çıktı gerekiyorsa WRITE s$, çok daha kısadır ?CHR$(34);s$;CHR$(34). Örneğin bkz . Bilinen en kısa QBasic quine .
  • Bir sayının çıktısını alırken, WRITEönceki ve sonraki gibi boşluk PRINTeklemez. WRITE ndaha kısadır ?MID$(STR$(n),2). Örneğin bkz. QB64'teki FizzBuzz .
  • Birden çok değer çıkarırken, WRITEvirgül: WRITE 123,"abc"çıkışlar ile ayırın 123,"abc". Bunun yararlı olacağı bir senaryo düşünemiyorum, ama bu bir tane olmadığı anlamına gelmiyor.

Sınırlamaları WRITE:

  • Gibi bir ayırıcı olmadan birden fazla değer çıkarmanın bir yolu yoktur PRINT a;b.
  • Çıktının sonunda satırsonu bastırmanın bir yolu yoktur. (Bu sorunu geçici olarak çözebilirsiniz LOCATE, ancak bu çok fazla bayttır.)

1

Bazen, QBasic girdileri işlevlere yönlendirir. İstismar et!

Dizeler yerine karakterler üzerinde çalışan birkaç işlev vardır, ancak charQBasic'te bir veri türü yoktur, sadece string ($)tür vardır. Örneğin, ASC()bir karakterin ASCII anahtar kodunu döndüren işlevi ele alalım . Eğer girersek

PRINT ASC("lala")

sadece birincisi lQBasic tarafından dikkate alınacaktır. Bu şekilde, uzunluğu 1'e kadar olan bir ipi kesmekle uğraşmak zorunda kalmıyoruz.

Başka bir örnek , fonksiyonun cevaplardan birinde kullanıldığı bu sorudan gelir STRING$().

STRING $ işlevi, n sayısı ve s $ dizesi olmak üzere iki bağımsız değişken alır ve s $ karakterinin ilk karakterinin n kopyasından oluşan bir dize oluşturur

@DLosc, burada

QBasic'in çok karakterli bir dize sunulduğunda ve yalnızca bir karakter gerektirdiğinde, otomatik olarak ilk karakteri alır ve geri kalanını yok sayar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.