Jakie masz ogólne wskazówki na temat gry w golfa w QBasic? Szukam pomysłów, które można by zastosować do ogólnych problemów z golfem, które są przynajmniej nieco specyficzne dla QBasic (np. „Usuń komentarze” nie jest odpowiedzią).
Mile widziane są również wskazówki dotyczące emulatora QB64 . Ma kilka dodatkowych funkcji, których nie ma w Microsoft QBasic.
Odpowiedzi:
Poznaj swoje konstrukcje zapętlające
QBasic ma kilka konstruktów zapętlenie:
FOR ... NEXT
,WHILE ... WEND
, iDO ... LOOP
. Możesz także użyćGOTO
lub (w niektórych sytuacjach)RUN
do zapętlenia.FOR ... NEXT
jest całkiem dobry w tym, co robi. W przeciwieństwie do Pythona jest prawie zawsze krótszy niż odpowiednikWHILE
lubGOTO
pętla, nawet jeśli robi się trochę bardziej fanatyczny:Pamiętaj, że nie musisz powtarzać nazwy zmiennej po
NEXT
, i możesz wyeliminować spację między liczbami a większością następujących słów kluczowych.WHILE ... WEND
jest dobre, gdy masz pętlę, która może wymagać wykonania 0 razy. Ale jeśli wiesz, że pętla zostanie wykonana co najmniej raz,GOTO
może być o jeden bajt krótszy:DO ... LOOP
do nieskończonych pętli (z wyjątkiem tego, gdzieRUN
zamiast tego można użyć). Chociaż kosztuje tyle samo znaków co bezwarunkowyGOTO
, jest nieco bardziej intuicyjny w czytaniu. (Zauważ, że „nieskończona pętla” może obejmować pętle, które można zerwać za pomocą aGOTO
.) SkładniaDO WHILE
/DO UNTIL
/LOOP WHILE
/LOOP UNTIL
jest zbyt szczegółowa; lepiej użyćWHILE
lubGOTO
odpowiednio.GOTO
jest, jak wspomniano powyżej, najkrótszym ogólnym sposobem na napisanie pętli do / while. Użyj jednocyfrowych numerów linii zamiast etykiet. Zauważ, że gdy aGOTO
jest jedynąTHEN
częściąIF
instrukcji, dostępne są dwie równie zwięzłe składnie skrótów:GOTO
może być również wykorzystywany do tworzenia bardziej skomplikowanych przepływów sterowania . Naysayers nazywają to „kodem spaghetti”, ale to jest kod golfowy: nieczytelność jest prawie cnotą!GOTO
duma!RUN
jest przydatny, gdy trzeba przeskoczyć do ustalonego miejsca w programie i nie trzeba zachowywać żadnej z wartości zmiennych.RUN
sam zrestartuje program od góry; z etykietą lub numerem linii, uruchomi się ponownie w tym wierszu. Użyłem go głównie do tworzenia bezstanowych nieskończonych pętli .źródło
Użyj skrótów dla
PRINT
iREM
Możesz użyć
?
zamiastPRINT
i'
zamiastREM
(komentarz).'
może się również przydać przy poliglocie z językami, które obsługują'
jako część składni char lub string.źródło
Badanie podzielności
W programach wymagających przetestowania, czy jedna liczba całkowita jest podzielna przez inną, oczywistym sposobem jest użycie
MOD
:Ale krótszym sposobem jest użycie podziału na liczby całkowite:
Oznacza to, że
x
int-div3
równa sięx
float-div3
.Zauważ, że oba te podejścia powrócą
0
dla falsey i-1
dla prawdy, więc może być konieczne zanegowanie wyniku lub odjęcie go zamiast dodawania.Jeśli potrzebujesz przeciwnego warunku (tzn. Nie
x
jest on podzielny przez3
), oczywistym podejściem jest użycie operatora nierówności:Ale jeśli
x
gwarantujemy, że będzie to nieujemne, możemy zapisać bajt. Podział liczb całkowitych obcina wynik, więc zawsze będzie mniejszy lub równy podziałowi zmiennoprzecinkowemu. Dlatego możemy napisać warunek jako:Podobnie, jeśli
x
gwarantuje się , że jest ujemna, obcięcie zwiększa wynik i możemy pisaćx\3>x/3
. Jeśli nie znasz znakux
, musisz się trzymać<>
.źródło
Nadużycie skanera
Podobnie jak w wielu językach, ważne jest, które znaki można, a których nie można usunąć.
IF""=a$THEN?0
FOR i=1TO 10STEP 2
. Istnieją pewne różnice między QBasic 1.1 (dostępnym na archive.org ) a QB64 :?123x
staje sięPRINT 123; x
. Wyjątkami od powyższego są sekwencje podobne do1e2
i1d+3
, które są traktowane jako notacja naukowa i rozszerzone do100!
i1000#
(odpowiednio pojedynczej i podwójnej precyzji).d
,e
lubf
w ogóle, chyba że są one częścią dobrze uformowanej notacji naukowej dosłownym. (Na przykład nie można pominąć spacji po numerze wiersza w1 FOR
lub9 END
, podobnie jak w QBasic.). Odmienia średniki w instrukcjach print tylko wtedy, gdy jedno z wyrażeń jest ciągiem:?123"abc"
działa, ale nie?TAB(5)123
lub?123x
.PRINT
instrukcji kończącej się wywołaniemTAB
lubSPC
. (QB64 nie.)0
można pominąć przed lub po przecinku (.1
lub1.
), ale nie jednocześnie (.
).ENDIF
jest równoważne zEND IF
.źródło
endif
faktycznie działa w QB64, zobacz tę odpowiedźPołącz
Next
wyciągiMoże być skondensowany do
gdzie iteratory dla
For
pętli sąi
,j
ik
- w tej kolejności.Na przykład poniżej (69 bajtów)
Może być skondensowany do 65 bajtów
I jeśli chodzi o to, jak wpływa to na formatowanie i wcięcia, myślę, że najlepszym podejściem do rozwiązania tego problemu jest wyrównanie następnej instrukcji z najbardziej zewnętrzną dla instrukcji. Na przykład.
źródło
Poznaj swoje metody wprowadzania
QBasic ma kilka sposobów, aby uzyskać dane z klawiatury użytkownik:
INPUT
,LINE INPUT
,INPUT$
, iINKEY$
.INPUT
jest standardową instrukcją wejścia wielofunkcyjnego. Program zatrzymuje to, co robi, wyświetla kursor i pozwala użytkownikowi wpisać dane wejściowe, zakończone przez Enter.INPUT
potrafi odczytywać liczby lub ciągi i odczytywać wiele wartości oddzielonych przecinkami. Możesz podać ciąg jako podpowiedź, możesz przejść do domyślnego podpowiedzi ze znakiem zapytania, a nawet (właśnie dowiedziałem się tego dzisiejszego wieczora) całkowicie wyłączyć monit. Niektóre przykładowe wywołania:INPUT x$,y
Używa domyślnego
?
monitu i odczytuje ciąg i liczbę, oddzielone przecinkami.INPUT"Name";n$
Monituje
Name?
i odczytuje ciąg.INPUT"x=",x
Monituje z
x=
(bez znaku zapytania! Zanotuj przecinek w składni) i odczytuje liczbę.INPUT;"",s$
Pomija monit (używając powyższej składni przecinka z pustym ciągiem zachęty), odczytuje ciąg i nie przechodzi do następnego wiersza, gdy użytkownik naciśnie klawisz Enter (to
INPUT
robi potem średnik ). Na przykład, jeśliPRINT s$
natychmiast po tym, twój ekran będzie wyglądałUser_inputUser_input
.INPUT
jest to, że nie można odczytać ciągu z przecinkiem, ponieważINPUT
używa on przecinka jako separatora pól. Aby odczytać pojedynczy wiersz dowolnych (drukowalnych ASCII) znaków, użyjLINE INPUT
. Ma takie same opcje składni jakINPUT
, z tym że pobiera dokładnie jedną zmienną, która musi być zmienną łańcuchową. Inną różnicą jest to, żeLINE INPUT
domyślnie nie wyświetla monitu; jeśli chcesz, musisz to wyraźnie określić.INPUT$(n)
nie wyświetla monitu ani kursora, ale po prostu czeka, aż użytkownik wprowadzin
znaki, a następnie zwróci ciąg zawierający te znaki. W przeciwieństwie doINPUT
lubLINE INPUT
, użytkownik nie musi naciskać Enterpóźniej, i w rzeczywistości Entermoże być jednym ze znaków (da znak ASCII 13, znany jako języki podobne do C\r
).Najczęściej jest to przydatne
INPUT$(1)
, zwykle w pętli.INPUT$
sprawdza się w interaktywnych programach, w których działają pojedyncze naciśnięcia klawiszy . Niestety działa tylko z kluczami, które mają kody ASCII; obejmuje to rzeczy takie jak Esci Backspace, ale nie klawisze strzałek, Inserti Delete, i inne.Który jest tam, gdzie
INKEY$
przychodzi. Jest podobny doINPUT$(1)
tego, że zwraca wyniki pojedynczego naciśnięcia klawisza 1 , ale różni się tym, że:INKEY$
nie przyjmuje żadnych argumentów.Podczas gdy
INPUT$(n)
zatrzymuje wykonywanie, dopóki użytkownik nie wprowadzin
znaków,INKEY$
nie wstrzymuje wykonywania. Jeśli użytkownik aktualnie naciska klawisz,INKEY$
zwraca ciąg znaków reprezentujący ten klawisz; jeśli nie, zwraca""
. Oznacza to, że jeśli chcesz użyćINKEY$
następnego naciśnięcia klawisza, musisz zawinąć go w pętlę zajętości : 2Zarówno
INPUT$
iINKEY$
znaków ASCII powrót do kluczy, które odpowiadają znaków ASCII (w tym znaków sterujących, takich jak ucieczka, zakładki, i Backspace). JednakINKEY$
może również obsługiwać niektóre klucze, które nie mają kodów ASCII. Dla nich (mówi plik pomocy) „INKEY $ zwraca 2-bajtowy ciąg złożony ze znaku null (ASCII 0) i kodu skanowania klawiatury”.Czyste jak błoto? Oto kilka przykładów. Jeśli użyjesz
INKEY$
powyższej pętli do przechwycenia naciśnięcia klawisza lewej strzałki,k$
będzie ona zawierać ciąg znaków"␀K"
(zK
reprezentującym kodem skanowania 75). Dla prawej strzałki to"␀M"
(77). Strona w dół to"␀Q"
(81). F5 to"␀?"
(63).Nadal czysty jak błoto? Tak. To nie jest najbardziej intuicyjna rzecz na świecie. Plik pomocy zawiera tabelę kodów skanowania, ale zawsze po prostu piszę mały program do drukowania wyników
INKEY$
i naciskam kilka klawiszy, aby dowiedzieć się, jakie są właściwe wartości. Kiedy już wiesz, które znaki odpowiadają danym klawiszom, możesz używaćRIGHT$(k$,1)
iLEN(k$)
rozróżniać wszystkie różne przypadki, które możesz napotkać.Dolna linia?
INKEY$
jest dziwne, ale jest to jedyna droga, jeśli Twój program wymaga nieblokujących danych wejściowych lub używa klawiszy strzałek .1 nie w tym Shift, Ctrl, Alt, PrntScr, Caps Lock, i podobne. Te się nie liczą. : ^ P
2 Ten
WHILE ... WEND
idiom jest tym, czego nauczyłem się w moich książkach QBasic. Dla celów golfa jednak pętla jest krótszy .GOTO
źródło
LOCATE może być naprawdę potężny
LOCATE
Zestawienie pozwala umieszczać nigdzie kursora na ekranie (w zwykłym 80x40 ograniczeń przestrzennych znaków) i wydrukować coś w tym miejscu. Ta odpowiedź na wyzwanie naprawdę to pokazuje (i jest również połączona z wieloma innymi wskazówkami z tego tematu).Wyzwanie wymaga od nas wypisania każdej postaci, którą użytkownik nacisnął w siatce 16 x 6. Ze
LOCATE
jest to po prostu kwestia div i mod na kodzie ASCII (a
w tym kodzie):A następnie wydrukowanie znaku:
źródło
W QBasic zwyczajowo używa się
DIM
instrukcji do tworzenia zmiennych, nadając im nazwę i typ. Jednak nie jest to obowiązkowe, QBasic może również wyprowadzić typ poprzez sufiks nazwy zmiennej. Ponieważ nie można jednocześnie deklarować i inicjalizować zmiennej, często rozsądnie jest pominąćDIM
kodek. Dwa fragmenty, które są funkcjonalnie identyczne *:* Uwaga: powoduje to utworzenie dwóch różnych nazw zmiennych.
Możemy określić typ zmiennej, dodając
$
na końcu nazwy zmiennej dla ciągów,!
liczb pojedynczej precyzji i%
podwójnych. Zakłada się single, gdy nie określono żadnego typu.Zauważ, że dotyczy to również tablic. Zwykle tablica jest definiowana jako:
Ale tablice również nie muszą być
DIM
med:a$
jest teraz tablicą dla łańcuchów z 11 miejscami: od indeksu 0 do włącznie z indeksem 10. Jest to zrobione, ponieważ QBasic ma opcję, która umożliwia indeksowanie tablic zarówno w oparciu o 0, jak i w oparciu o 1. Domyślna tablica obsługuje oba te sposoby.Pamiętasz tablicę dwudziestu gniazd, którą
DIM
wymieniliśmy powyżej? To faktycznie ma 21 gniazd, ponieważ ta sama zasada dotyczy zarówno przyciemnionych, jak i nieściemnionych tablic.źródło
IF
Oświadczenia skracająceIF
wyciągi są dość drogie, a ich gra w golfa pozwala zaoszczędzić wiele bajtów.Zastanów się, co następuje (na podstawie odpowiedzi Erika the Outgolfer):
Pierwszą rzeczą, którą możemy zrobić, to zapisać
ENDIF
za pomocąIF
instrukcji jednowierszowej :Działa to tak długo, jak nie próbujesz umieścić go w tym samym wierszu, co cokolwiek innego. W szczególności, jeśli masz zagnieżdżone
IF
instrukcje, tylko najbardziej wewnętrzna instrukcja może być jednowierszowa.Ale w tym przypadku możemy
IF
całkowicie wyeliminować matematykę. Zastanów się, czego tak naprawdę chcemy:RND<.5
jest to prawda (-1
), chcemy:x
zmniejszyć o 1y
pozostać niezmienionyma(i)
zostać 1RND<.5
jest to false (0
), chcemy:x
pozostać niezmienionymy
zmniejszyć o 1a(i)
stać się 0Teraz, jeśli mamy zachować wynik warunkowego w zmiennej (
r=RND<.5
), możemy obliczyć nowe wartościx
,y
oraza(i)
:r
to-1
,x=x-1
; kiedyr
to0
,x=x+0
.r
to-1
,y=y+0
; kiedyr
to0
,y=y-1
.r
to-1
,a(i)=1
; kiedyr
to0
,a(i)=0
.Nasz końcowy kod wygląda następująco:
oszczędność aż 20 bajtów (40%) w stosunku do oryginalnej wersji.
Metodę matematyczną można zaskakująco często stosować, ale gdy istnieje różnica w logice między tymi dwoma przypadkami (np. Gdy musisz wprowadzić coś w jednym przypadku, ale nie w drugim), nadal będziesz musiał użyć
IF
.źródło
Czasami należy unikać tablic
Tablice w QBasic, gdy są tworzone bez,
DIM
mają tylko 11 miejsc. Jeśli wyzwanie wymaga więcej niż 11 miejsc (lub N miejsc, gdzie N może być większy niż 11), powinieneś miećDIM
tablicę. Załóżmy również, że chcemy zapełnić tę tablicę danymi:Nawet w golfa może to zająć dużo miejsca. W takich przypadkach bajty mogą być tańsze:
Tutaj umieszczamy wszystko w 1 połączonym ciągu. Później uzyskujemy do niego dostęp w następujący sposób:
W tym podejściu ważne jest, aby wszystkie wartości były jednakowej długości. Wybierz najdłuższą wartość i wypisz wszystkie pozostałe:
Nie musisz wpisywać ostatniej wartości, a nawet możesz pominąć cudzysłowy zamykające! Jeśli wyzwanie określa, że białe znaki nie są dozwolone w odpowiedzi, użyj,
RTRIM$()
aby to naprawić.Można zobaczyć tę technikę w akcji tutaj .
źródło
PRINT
(?
) ma pewne dziwactwaLiczby są drukowane z wiodącą i końcową spacją.
Drukowanie dodaje podział linii. To zachowanie można zmienić, dodając przecinek na końcu instrukcji, aby zamiast tego wstawić tabulator, lub średnik, aby uniknąć wstawiania:
Podczas drukowania nie jest konieczne używanie
&
ani;
wykonywanie różnych operacji, np.?1"x"s$
wypisze numer1
ze spacjami po każdej stronie, literęx
i treśćs$
Wyjścia
Drukowanie podziału linii można wykonać za pomocą just
?
źródło
-
drukowany jest tam znak minus . Spacja jest również drukowana po numerze. Najlepszym sposobem, jaki odkryłem, aby pozbyć się tych przestrzeni jest - niePRINT USING
wiem, jeśli chcesz dodać to do tej odpowiedzi lub powinna to być osobna odpowiedź.WRITE
mogą być przydatne zamiastPRINT
PRINT
jest zwykle sposobem, w jaki chcesz robić dane wyjściowe, ponieważ jest dość elastyczny i ma?
skrót. JednakWRITE
polecenie może zaoszczędzić bajty w określonych sytuacjach:WRITE
zawija go w podwójne cudzysłowy ("
). Jeśli potrzebujesz wyniku z podwójnymi cudzysłowami,WRITE s$
jest znacznie krótszy niż?CHR$(34);s$;CHR$(34)
. Zobacz na przykład najkrótszą znaną quinę QBasic .WRITE
nie dodaje spacji przed i po niej, jak toPRINT
robi.WRITE n
jest znacznie krótszy niż?MID$(STR$(n),2)
. Zobacz na przykład FizzBuzz w QB64 .WRITE
oddziel je przecinkami:WRITE 123,"abc"
wyjścia123,"abc"
. Nie mogę wymyślić scenariusza, w którym byłoby to przydatne, ale to nie znaczy, że nie ma takiego.Ograniczenia
WRITE
:PRINT a;b
.LOCATE
, ale to kosztuje dużo bajtów.)źródło
Czasami QBasic zmienia dane wejściowe do funkcji. Nadużywaj tego!
Istnieje kilka funkcji, które działają na znakach zamiast ciągów, ale
char
w QBasic nie mastring ($)
typu danych, istnieje tylko typ. Weźmy na przykładASC()
funkcję, która zwraca kod ASCII dla znaku. Jeśli wejdziemytylko pierwszy
l
byłby brany pod uwagę przez QBasic. W ten sposób nie musimy zawracać sobie głowy przycinaniem sznurka do długości 1.Kolejny przykład pochodzi z tego pytania, w którym
STRING$()
funkcja jest używana w jednej z odpowiedzi.Zauważ, że QBasic, gdy oferowany jest ciąg zawierający wiele znaków i potrzebuje tylko jednego znaku, automatycznie przyjmuje pierwszy znak i ignoruje resztę.
źródło