Trở lại thứ Sáu: Làm mới danh sách BASIC phổ ZX của tôi


15

Ngôn ngữ lập trình đầu tiên tôi tiếp xúc là Sinclair BASIC . Giống như nhiều phương ngữ BASIC, nó yêu cầu tất cả các dòng mã nguồn được đánh số .

Kết quả là, sử dụng GO TO lệnh là thành ngữ và nhảy thực thi đến số dòng đã cho (không có nhãn).

Ngoài ra có một GO SUBlệnh liên quan có thể được sử dụng như một lệnh gọi hàm thô sơ. Một lần nữa, thực thi nhảy đến số dòng đã cho, nhưng khi RETURNđạt được lệnh, thực thi sẽ nhảy trở lại lệnh tiếp theo sau GO SUB.

Tương tự RUNlệnh sẽ khởi động lại thực hiện chương trình tại dòng đã cho.

Bất cứ ai đã dành bất kỳ thời gian nào trong trình thông dịch BASIC được đánh số dòng sẽ học cách sử dụng sơ đồ đánh số với các khoảng trống trong đó. Điều này là để dễ dàng hơn để chèn các dòng mã mới. Tuy nhiên, ngay cả khi đó, bạn vẫn có thể thấy mình cần chèn các dòng mới vào giữa các dòng được đánh số liên tiếp.


Đưa ra một danh sách BASIC được đánh số dòng làm đầu vào, xuất ra cùng một chương trình nhưng được đánh số lại sao cho số dòng bắt đầu ở mức 10 và tăng theo các bước 10. Danh sách đầu vào có thể có GO TOhoặc GO SUBcác lệnh, do đó, các số được liên kết với chúng cũng phải được điều chỉnh.

  • GO TOGO SUBcác lệnh hoặc là trên dòng riêng của họ hoặc ở cuối IF THENdòng. Nó an toàn để nói ^(\d+) .*GO (TO|SUB) (\d+)$là đủ để phù hợp với dòng như vậy. Các lệnh trong dấu ngoặc kép nên được bỏ qua.

  • RUNcác lệnh sẽ luôn nằm trên các dòng riêng của chúng. Trong trường hợp này, một số dòng là tùy chọn. Nếu nó bị thiếu, thì trình thông dịch chỉ cần bắt đầu ở đầu chương trình.

  • Nếu một GO TO, GO SUBhoặc RUNlệnh tham chiếu một dòng không tồn tại, thì thay vào đó, nó sẽ nhảy đến dòng được xác định tiếp theo. Mục nhập của bạn cần phải giải quyết vấn đề này và đảm bảo mọi tham chiếu dòng như vậy được cố định để chúng trỏ đến đúng dòng. Hành vi có thể không được xác định nếu một số dòng sau khi kết thúc chương trình được đưa ra trong một trong các lệnh này.

  • Số dòng sẽ luôn là số nguyên dương từ 1 đến 9999 (theo hướng dẫn). Điều này có nghĩa là các chương trình đầu vào sẽ không bao giờ có nhiều hơn 999 dòng.

  • Các dòng đầu vào sẽ luôn được đánh số theo thứ tự tăng dần về số lượng.

  • Đối với mục đích của thách thức này, danh sách đầu vào sẽ chỉ chứa ASCII có thể in được. Bạn không cần phải lo lắng về bộ ký tự ZX. Như đã nói, nếu mục nhập của bạn thực sự được viết bằng ZX BASIC hoặc mã máy / lắp ráp z80 thích hợp (và có các trình giả lập ngoài đó ), thì bạn có thể chọn đầu vào của mình được mã hóa trong bộ ký tự ZX .

  • Bạn không được sử dụng bất kỳ thư viện hoặc tiện ích nào được thiết kế riêng cho mục đích này.

Ví dụ đầu vào:

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"

Kết quả ví dụ:

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"

Tôi muốn liên kết đến một hướng dẫn ZX BASIC. Điều tốt nhất tôi có thể tìm thấy dường như là http://www.wworldofspectrum.org/ZXBasicManual/index.html nhưng đây dường như là một liên kết chết. Máy wayback có một bản sao mặc dù .


7
Cũng kết luận về việc đặt câu hỏi thứ 5000!
FryAmTheEggman

1
Thời gian hoài cổ - PC đầu tiên của tôi là Spectrum 48K và một trong những chương trình lắp ráp đầu tiên của tôi là máy đổi mới
edc65

2
@ edc65 Bạn vẫn có mã lắp ráp số? Nếu vậy bạn rất hoan nghênh đăng nó như một câu trả lời!
Chấn thương kỹ thuật số

1
Trường hợp thử nghiệm phải bao gồm ít nhất một goto / gosub trong một chuỗi ký tự.
Peter Taylor

1
Tôi đã tìm thấy một đề cập: ZX81 cho phép các GOTO và GOSUB được tính toán như trongGOTO 100 + A*10Phụ lục C của Hướng dẫn sử dụng phổ ZX liệt kê GO TOlà chấp nhận biểu thức số (không giới hạn đối với các hằng số). Đây là một cuộc thảo luận về giá trị của tính toán GOTOtrên ZX80 và ZX81. BTW, tôi không biết tại sao không gian được thêm vào trong phiên bản Spectrum.
Toby Speight

Câu trả lời:


5

JavaScript (ES6) 177

Chỉnh sửa Đã thêm quét (tốn kém) cho số dòng hợp lệ tiếp theo

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`
`

KIỂM TRA

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
Có vẻ tốt. Giá trị +1 của tôi :)
Chấn thương kỹ thuật số

2

Perl 6, 147 145 144 142 byte

{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}})}

Điều này có thể có thể được đánh golf xuống một chút nữa.

Mở rộng

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} } 
            )
}

không có lý do để sử dụng phương pháp .min. sử dụng {min %line-map.keys».Num.grep:*>=$1thay thế
Ven

1

Visual Basic cho Ứng dụng, 288 byte

Tôi không thể cưỡng lại việc đưa ra một giải pháp theo phương ngữ BASIC. Có thể hoạt động với Visual Basic 6 / .NET hoặc các biến thể hiện đại khác với những thay đổi nhỏ.

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

Tôi đã sử dụng rất nhiều biến số một chữ cái cho sự đồng nhất. Ngoài ra, tôi đã nén tất cả các khoảng trắng không cần thiết (VBE mở rộng chúng tự động khi nhập). Số byte là cho tệp .BAS cuối cùng, với CHR (10) là dòng mới.

Chương trình con, có thể được gọi từ cửa sổ VBE ngay lập tức, mở chương trình BASIC của BASlair (tham số đầu tiên là đường dẫn đến tệp ASCII - với CHR (10) là dòng mới - chứa chương trình), đánh số lại các dòng và ghi kết quả vào biến Biến (tham số thứ hai).

Ý tưởng là lặp lại trên tất cả các số dòng nguồn có thể, thứ tự tăng dần và cho mỗi số, thay thế cùng một lúc tất cả các số dòng phù hợp cũng như GO TO, GO SUBRUN tài liệu tham khảo với số dòng mục tiêu tiếp theo có sẵn. Sử dụng phương pháp này, chúng tôi không cần bất kỳ loại bảng dịch. Số dòng đích được tăng lên mỗi khi tìm thấy kết quả khớp với số dòng nguồn, do đó, các tham chiếu dòng "sai" được điều chỉnh tự động thành số hợp lệ tiếp theo. Các ký tự dòng mới được sử dụng làm điểm đánh dấu bắt đầu và kết thúc dòng và CHR (0) - không bao giờ được sử dụng trong chương trình vì nó không thể in được - được sử dụng làm điểm đánh dấu tạm thời, để tránh đánh số lại cùng một dòng nhiều lần.

Một số nhận xét:

  • Để đơn giản, chúng tôi sử dụng chuỗi nhỏ hơn có thể cho một trận đấu với các câu lệnh nhảy. Sử dụng dòng cuối trên chuỗi tìm kiếm của chúng tôi, chúng tôi không gặp rủi ro bao gồm các lần xuất hiện được trích dẫn hoặc các hàm người dùng (luôn sử dụng dấu ngoặc đơn trong Sinclair). GO TOyêu cầu một chuỗi lớn hơn vì FOR ... TOcấu trúc (ví dụ so sánh 50 FOR X=AGO TO 10050 GO TO 100)

  • Mã này không hỗ trợ các câu lệnh ở dạng GO TO200(không có khoảng trắng), mặc dù hướng dẫn ZX ngụ ý rằng đó là mã hợp lệ trên một số ví dụ (Sẽ tốn thêm hàng tá byte để xử lý nó).

  • Mã này thêm một dòng mới ở đầu và một dòng khác ở cuối chương trình. Cuối cùng tôi có thể xóa cái này (một tá byte nữa) nhưng con số ZX có thể sẽ bỏ qua các dòng trống.

Dưới đây, một phiên bản dễ đọc hơn:

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

BTW, một giải pháp QBasic sẽ lâu hơn nhiều, vì QBasic không có chức năng thay thế chuỗi được xây dựng theo như tôi nhớ.
DLosc

Tôi nghĩ bạn đã đúng ... quên về điều đó
dnep

1

Pip -rn , 63 byte

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

Hãy thử trực tuyến!

Giải trình

Thiết lập

Các -rcờ đọc tất cả các thiết bị nhập chuẩn và lưu trữ nó như là một danh sách các dòng trong biến cục bộ g. Biến toàn cục tđược preinitialized thành 10 và biến toàn cục sđược preinitialized thành " ".

Yg

Sắp xếp danh sách các dòng gvào biến toàn cục y, sao cho nó có sẵn bên trong hàm chúng ta sắp xác định.

Chức năng dịch số dòng

Chúng tôi xây dựng một hàm ánh xạ từ bất kỳ số dòng nào trong sơ đồ đánh số gốc (bao gồm cả số không tồn tại) thành số dòng tương ứng trong sơ đồ đánh số mới.

Giả sử chúng ta có những dòng này:

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

Chúng tôi muốn ánh xạ 1 đến 10, 2-4 đến 20 và 5-9 đến 30. Nếu chúng tôi có một danh sách các số dòng gốc ([1; 4; 9] ), chúng tôi có thể sử dụng thao tác bộ lọc để tìm ra số lượng các số này ít hơn hơn số dòng chúng tôi đang cố gắng chuyển đổi. Nhân kết quả đó với 10 và thêm 10, và chúng tôi có câu trả lời mong muốn.

Ví dụ: khi chuyển đổi 9, có hai số dòng (1 và 4) nhỏ hơn 9. 2 * 10 + 10 cho 30. Khi chuyển đổi 3, có một số dòng (1) nhỏ hơn 3. 1 * 10 + 10 cho 20.

Đây là mã (được sửa đổi một chút để dễ đọc hơn):

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

Thay thế đầu tiên: GO TO, GO SUB, vàRUN

Phần còn lại của chương trình là một biểu thức duy nhất thực hiện gvà thực hiện một vài thay thế regex (vector hóa, áp dụng cho từng dòng trong g).

Đây là sự thay thế đầu tiên:

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

Regex khớp với bất kỳ RUN, GO SUBGO TO, theo sau là một số, theo sau là cuối dòng. Điều này đảm bảo nó không khớp với các chuỗi bên trong và cũng không khớp với RUNsố dòng.

Thứ tự của các nhóm bắt giữ là quan trọng. Nhóm đầu tiên nắm bắt được lệnh (một trong RUN, GO SUBhoặc GO TO). Nhóm thứ hai, nếu được sử dụng, sẽ chụp SUBhoặc TO. Chúng ta không cần phải nắm bắt phần này, nhưng một nhóm không bắt giữ sẽ cần thêm byte. Sau đó, nhóm thứ ba chụp số dòng.

Chúng tôi sử dụng chức năng gọi lại để thay thế. Với chức năng gọi lại trong Pip, toàn bộ trận đấu là đối số đầu tiên a, và các nhóm chụp theo thứ tự là các đối số tiếp theo b, c, d, và e. Vì vậy, chúng ta có lệnh trong nhóm đầu tiên, đi vào bvà số dòng trong nhóm thứ ba, đi vào d. Thay đổi duy nhất chúng ta cần thực hiện là chuyển số dòng thông qua chức năng chuyển đổi, được gọi là kiểu Lisp : (nd). Sau đó, chúng tôi kết hợp nó cùng với bmột không gian và trả lại nó.

Thay thế thứ hai: số dòng

Tất cả những gì còn lại để chuyển đổi là số dòng ở đầu dòng.

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

Regex khớp với một chuỗi các chữ số ở đầu một dòng. Một lần nữa chúng ta sử dụng chức năng gọi lại; lần này, hàm chuyển đổi nlà đủ, vì toàn bộ khớp (đối số đầu tiên a) là số chúng tôi muốn chuyển đổi.

Vì đây là biểu thức cuối cùng trong chương trình, Pip tự động in kết quả. Các -ncờ tách danh sách kết quả với dòng mới.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.