П'ятниця: Перенумеруйте мій список ZX-спектру BASIC


15

Першою мовою програмування, до якої я був підданий, був Sinclair BASIC . Як і багато діалектів BASIC, він вимагає нумерації всіх рядків вихідного коду .

В результаті використання GO TOкоманди було ідіоматичним і переходить до заданого номера рядка (без міток).

Також є пов'язана GO SUBкоманда, яка може використовуватися як виклик рудиментарної функції. Знову виконується перехід до заданого номера рядка, але коли RETURNкоманда буде досягнута, виконання переходить до наступної інструкції після GO SUB.

Аналогічно RUNкоманда перезапустить виконання програми в заданому рядку.

Кожен, хто провів будь-який час в інтерпретаторі BASIC з рядковими номерами, навчиться використовувати схему нумерації з пробілами. Це так, що простіше вставити нові рядки коду. Однак навіть тоді ви все ще можете виявити необхідність вставляти нові рядки між послідовно пронумерованими рядками.


Враховуючи рядки BASIC з номером рядків як вхідні дані, виведіть ту саму програму, але перенумеровану таким чином, що номери рядків починаються з 10 і збільшуються на кроках 10. Лістинг вхідних даних може мати GO TOабо GO SUBкоманди, тому номери, пов'язані з цим, також повинні бути відрегульовані.

  • GO TOі GO SUBкоманди знаходяться або на власних рядках, або в кінці IF THENрядків. Безпечно сказати ^(\d+) .*GO (TO|SUB) (\d+)$, достатньо, щоб відповідати таким рядкам. Ці команди в лапках слід ігнорувати.

  • RUNкоманди завжди будуть у власних рядках. У цьому випадку номер рядка необов’язковий. Якщо його немає, то перекладач просто починається у верхній частині програми.

  • Якщо через GO TO, GO SUBабо RUNпосилання команди неіснуючої рядок, то замість цього перейти до наступної певної лінії. Ваш запис повинен вирішити це питання та забезпечити фіксацію таких посилань на рядки, щоб вони вказували на правильний рядок. Поведінка може бути невизначеною, якщо номер рядка після закінчення програми вводиться в одній з цих команд.

  • Номери рядків завжди будуть додатними цілими числами від 1 до 9999 (відповідно до посібника). Це означає, що програми введення ніколи не матимуть більше 999 рядків.

  • Рядки вводу завжди будуть нумеровані у порядку зростання чисельно.

  • Для цілей цього виклику вхідні списки містять лише друкований ASCII. Вам не потрібно турбуватися про набір символів ZX. Сказавши , що, якщо ваша запис на насправді написано в ZX BASIC або відповідні z80 збірки / машинний код (і є емулятори з там ), то ви можете вибрати для вашого введення , щоб бути закодоване в наборі символів ZX замість.

  • Ви не можете використовувати будь-які бібліотеки чи утиліти, які спеціально підібрані для цієї мети.

Приклад введення:

1 REM "A rearranged guessing game"
2 INPUT A: CLS
3 INPUT "Guess the number ", B
10 IF A=B THEN PRINT "Correct": STOP
100 IF A<B THEN GO SUB 125
120 IF A>B THEN GO SUB 122
121 GO TO 3
125 PRINT "Try again"
126 RETURN
127 REM "An example of GO TO 7 and GO SUB 13 in quotes"

Приклад Вихід:

10 REM "A rearranged guessing game"
20 INPUT A: CLS
30 INPUT "Guess the number ", B
40 IF A=B THEN PRINT "Correct": STOP
50 IF A<B THEN GO SUB 80
60 IF A>B THEN GO SUB 80
70 GO TO 30
80 PRINT "Try again"
90 RETURN
100 REM "An example of GO TO 7 and GO SUB 13 in quotes"

Я хотів зв’язати посібник із ZX BASIC. Найкраще, що я міг знайти, здається, http://www.worldofspectrum.org/ZXBasicManual/index.html, але це, здається, є мертвим посиланням. Хоча машина зворотного зв'язку має копію .


7
Також приєднуються на запитання 5000-го питання!
FryAmTheEggman

1
Час ностальгії - на моєму першому ПК був Spectrum 48K, і однією з моїх програм першої збірки був перенумерувач
edc65

2
@ edc65 У вас ще є код складання переномери? Якщо так, то ви будете дуже раді залишити це як відповідь!
Цифрова травма

1
Тестовий випадок повинен містити щонайменше один goto / gosub в літеральному рядку.
Пітер Тейлор

1
Я знайшов згадку: ZX81 дозволяє обчислювати GOTOs та GOSUB, якGOTO 100 + A*10 і в Додатку C до Посібника з спектру ZX, перераховує GO TOяк прийняття числового виразу (без обмеження констант). Ось обговорення достоїнств обчислених GOTOна ZX80 та ZX81. До речі, я не маю уявлення, чому простір було додано у версії Spectrum.
Toby Speight

Відповіді:


5

JavaScript (ES6) 177

Редагування Додано (дороге) сканування наступного дійсного номера рядка

l=>l.split`
`.map((x,i)=>([,n,t]=x.match(/(\d+)(.*)/),l[n]=10*-~i,t),l=[]).map((x,i)=>10*-~i+x.replace(/(UN |GO TO |UB )(\d+)$/,(a,b,c)=>(l.some((v,i)=>i<c?0:a=b+v),a))).join`
`

ТЕСТ

f=l=>
  l.split`\n`
  .map((x,i)=>([,n,t]=x.match(/(\d+)(.*)/),l[n]=10*-~i,t),l=[])
  .map((x,i)=>10*-~i+x.replace(/(UN |GO TO |UB )(\d+)$/,(a,b,c)=>(l.some((v,i)=>i<c?0:a=b+v),a)))
  .join`\n`
        
//TEST
console.log=x=>O.textContent+=x+'\n'
  
test=`1 REM "A rearranged guessing game"
2 INPUT A: CLS
3 INPUT "Guess the number ", B
10 IF A=B THEN PRINT "Correct": STOP
100 IF A<B THEN GO SUB 125
120 IF A>B THEN GO SUB 122
121 GO TO 3
125 PRINT "Try again"
126 RETURN`
console.log(test+'\n\n'+f(test))
<pre id=O></pre>


1
Виглядає чудово. Мій +1 стоїть :)
Digital Trauma

2

Perl 6, 147 145 144 142 байт

{my%a;.trans(/^^(\d+)/=>{%a{$0}=$+=10}).trans(/:s<!after \"\N*>(UN |GO TO |UB )(\d+)<!before \N*\">/=>{$0~%a{%a.keys».Num.grep(*>=$1).min}})}

Це, ймовірно, може бути трохи більше в гольф.

Розширено

my &f = -> $s { 
    my %line-map; # This will map the old line numbers to the new ones

    $s.trans(/^^(\d+)/                    # This .trans creates the line number map
             => { %line-map{$0} = $+=10 } # as well as replaces the actual line numbers
            )\
      # This .trans replaces all the line numbers for each GO TO, GO SUB, RUN
      .trans(/:s<!after \"\N*>(UN |GO TO |UB )(\d+)<!before \N*\">/ 
             => {$0 ~ %line-map{%line-map.keys».Num.grep(*>=$1).min} } 
            )
}

немає підстав використовувати метод .min. використовувати {min %line-map.keys».Num.grep:*>=$1замість цього
вр

1

Visual Basic для додатків, 288 байт

Я не міг протистояти тому, щоб дати рішення на базічному діалекті. Можливо, працює з Visual Basic 6 / .NET або іншими сучасними варіантами з незначними змінами.

Sub n(t,a)
f=Chr(10)
u=Chr(0)
Open t For Input As 1
a=f &Input(LOF(1),1)&f
Close
j=10
For i=1 To 9999
q=f &j &u
g=" GO TO "
w=i &f
m=j &f
a=Replace(Replace(Replace(Replace(a,g &w,g &m),f &i &" ",q),"B "&w,"B "&m),"UN "&w,"UN "&m)
If InStr(1,a,q)Then j=j+10
Next
a=Replace(a,u," ")
End Sub

Я використовував дуже багато однобуквенних змінних для стислість. Також я придушив усі непотрібні пробіли (VBE автоматично розширює їх при імпорті). Кількість байтів призначена для остаточного файлу .BAS, з новим рядком CHR (10).

Підпрограма, яку можна викликати з безпосереднього вікна VBE, відкриває програму Sinclair BASIC (перший параметр - шлях до файлу ASCII - з CHR (10) як новий рядок - містить програму), перенумеровує рядки та записує результати у змінну змінної (другий параметр).

Ідея полягає в тому , щоб ітерація по всіх можливих номерами джерело лінії, порядок зростання, і для кожного з них, замінити відразу всі номери узгодження лінії, а також GO TO, GO SUBі RUNпосилання з наступним доступним номером цільової лінії. Використовуючи такий підхід, нам не потрібна будь-яка таблиця перекладу. Номер цільового рядка збільшується щоразу, коли знайдено збіг у вихідному номері рядка, тому "неправильні" посилання рядків автоматично підлаштовуються під наступне дійсне число. Символи нового рядка використовуються як маркери початку і кінця рядка, а CHR (0) - ніколи не використовується в програмі, оскільки він не для друку - використовується як тимчасовий маркер, щоб уникнути повторного переносування одного і того ж рядка кілька разів.

Деякі зауваження:

  • Для стиснення ми використовуємо менший можливий рядок для відповідності висловлюванням про стрибки. Використовуючи кінцевий рядок у наших рядках пошуку, ми не ризикуємо включати цитовані випадки або функції користувача (які завжди використовують дужки в Sinclair). GO TOвимагає більшого рядка через FOR ... TOконструкцію (наприклад, порівняти 50 FOR X=AGO TO 100та 50 GO TO 100)

  • Код не підтримує заяви у формі GO TO200(без пробілів), хоча керівництво ZX означає, що він є дійсним кодом на кількох прикладах (для цього обійдеться в десяток більше байтів).

  • Код додає новий рядок на початку та ще один в кінці програми. Я міг би це очистити в підсумку (десяток більше байтів), але думаю, що ZX, ймовірно, ігнорує порожні рядки.

Нижче, більш читана версія:

Sub Renumber(ByVal ProgramPath As String, ByRef Program As Variant)

    Open ProgramPath For Input As #1
    Program = Chr(10) & Input(LOF(1), 1) & Chr(10)
    Close

    NewNumber = 10
    For OldNumber = 1 To 9999
        Program = Replace(Program, " GO TO" & OldNumber & Chr(10), " GO TO" & NewNumber & Chr(10)) 'self-explaining
        Program = Replace(Program, Chr(10) & OldNumber & " ", Chr(10) & NewNumber & Chr(0)) 'matches line number (and replaces whistespace with Chr(0) to avoid re-replacing
        Program = Replace(Program, "B " & OldNumber & Chr(10), "B " & NewNumber & Chr(10)) 'matches GO SUB
        Program = Replace(Program, "UN " & OldNumber & Chr(10), "UN " & NewNumber & Chr(10)) 'matches RUN
        If InStr(1, Program, Chr(10) & NewNumber & Chr(0)) Then NewNumber = NewNumber + 10 'if there is such a line, increment NewNumber
Next
Program = Replace(Program, Chr(0), " ") 'replace back Chr(0) with whitespace
End Sub

До речі, рішення QBasic було б набагато довше, оскільки QBasic не має вбудованої функції заміни рядків, наскільки я пам'ятаю.
DLosc

Я думаю, ти маєш рацію ... забув про це
день

1

Піп -rn , 63 байти

Ygn:#{_<aFIy}*t+tgR`(RUN|GO (SUB|TO)) (\d+)$`{b.s.(nd)}R`^\d+`n

Спробуйте в Інтернеті!

Пояснення

Налаштування

-rПрапор читає все і стандартного введення зберігає його у вигляді списку рядків в локальній змінній g. Глобальна змінна tпопередньо ініціалізується до 10, а глобальна змінна sпопередньо ініціалізується до " ".

Yg

Перетворює список рядків gу глобальну змінну y, щоб він був доступний у функції, яку ми маємо визначити.

Функція перекладу номера рядка

Ми побудуємо функцію, яка відображає будь-який номер рядка в початковій схемі нумерації (включаючи неіснуючий) до відповідного номера рядка в новій схемі нумерації.

Припустимо, у нас є такі рядки:

1 INPUT A
4 PRINT A
9 IF A=1 THEN GO TO 3

Ми хочемо зіставити 1 до 10, 2-4 до 20 та 5-9 до 30. Якщо у нас є список вихідних номерів рядків ( [1; 4; 9]), ми можемо скористатися операцією фільтрації, щоб дізнатися, скільки цих чисел менше ніж номер рядка, який ми намагаємося перетворити. Помножте цей результат на 10 і додайте 10, і у нас є бажана відповідь.

Наприклад, при перетворенні 9 є два числа рядків (1 і 4) менше 9. 2 * 10 + 10 дає 30. При перетворенні 3 є номер рядка (1) менше 3. 1 * 10 + 10 дає 20.

Ось код (трохи змінений, щоб його було легше читати):

n:{#(_<aFIy)*t+t}
  {             }  Lambda function with parameter a:
        FIy         Filter y (the list of program lines) for
     _<a             lines that are numerically less than a
                    (In a numeric context, only the first run of digits on the line is considered)
   #(      )        Number of items in the filtered list
            *t+t    Times 10, plus 10
n:                 Assign that function to global variable n

Перша заміна: GO TO, GO SUBіRUN

Решта програми - це єдиний вираз, який займає gі робить пару замінників регулярних виразів (які векторизуються, застосовуючи до кожного рядка в g).

Ось перша заміна:

g R `(RUN|GO (SUB|TO)) (\d+)$` {b.s.(nd)}

Зворотний вираз відповідає будь-якому з RUN, GO SUBа GO TOза ним числом, а потім кінцем рядка. Це гарантує, що воно не відповідає внутрішнім рядкам, а також не відповідає RUNномеру рядка.

Порядок захоплення груп важливий. Перша група захоплює команду (один з RUN, GO SUB, або GO TO). Друга група, якщо вона використовується, фіксує SUBабо TO. Нам не потрібно захоплювати цю частину, але група, яка не захоплює, потребує додаткових байтів. Потім третя група фіксує номер рядка.

Для заміни використовуємо функцію зворотного дзвінка. З зворотним викликом функцією в Піп, весь матч є першим аргументом a, і захоплення група в порядку , є наступними аргументами b, c, dі e. Отже, у нас є команда в першій групі, яка входить b, і номер рядка в третій групі, в яку входить d. Єдина зміна , яке ми повинні зробити це , щоб передати номер рядка через нашу функцію перетворення, яка називається Lisp-стиль: (nd). Тоді ми об'єднуємо це разом із bпробілом і повертаємо його.

Друга заміна: номери рядків

Залишилося конвертувати лише номери рядків на початку рядків.

(...) R `^\d+` n

Зворотний вираз відповідає збігу цифр на початку рядка. Знову ми використовуємо функцію зворотного дзвінка; цього часу самої функції перетворення nдостатньо, оскільки вся відповідність (перший аргумент a) - це число, яке ми хочемо перетворити.

Оскільки це останній вираз у програмі, Pip автодруковує результат. -nПрапор відокремлює список результатів з новим рядком.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.