金曜日の先祖返り:ZXスペクトラムBASICリストの番号を付け直します


15

私が最初に触れたプログラミング言語はSinclair BASICでした。多くのBASIC方言と同様に、すべてのソースコード行に番号を付ける必要があります。

その結果、GO TOコマンドの使用は慣用的であり、実行を指定された行番号(ラベルなし)にジャンプします。

またGO SUB、基本的な関数呼び出しとして使用できる関連コマンドがあります。繰り返しますが、実行は指定された行番号にジャンプしますが、RETURNコマンドに到達すると、実行はの後の次の命令にジャンプして戻りGO SUBます。

同様に、RUNコマンドは指定された行でプログラムの実行を再開します。

行番号の付いたBASICインタープリターで時間を過ごした人は誰でも、番号にギャップのある番号付けスキームを使用することを学びます。これは、新しいコード行を挿入しやすくするためです。ただし、それでも、連続した番号の行の間に新しい行を挿入する必要がある場合があります。


入力として行番号付きのBASICリストを指定し、同じプログラムを出力しますが、行番号が10から始まり、10ずつ増加するように番号を付け直します。入力リストにはGO TOor GO SUBコマンドがあるため、これらに関連付けられた番号も調整する必要があります。

  • GO TOまた、GO SUBコマンドは独自の行または行末にありIF THENます。その^(\d+) .*GO (TO|SUB) (\d+)$ような行に一致するには、言うまでもなく安全です。引用符で囲まれたこれらのコマンドは無視する必要があります。

  • RUNコマンドは常に独自の行にあります。この場合、行番号はオプションです。欠落している場合、インタープリターは単にプログラムの先頭から開始します。

  • GO TOGO 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
懐かしさの時間-私の最初のPCはSpectrum 48Kで、私の最初のアセンブリプログラムの1つは
リナンバラーでした-edc65

2
@ edc65番号を変更するアセンブリコードはまだありますか?その場合は、回答として投稿してください。
デジタル外傷

1
テストケースでは、文字列リテラルに少なくとも1つのgoto / gosubを含める必要があります。
ピーターテイラー

1
私は言及を見つけた:ZX81は同様に計算型GOTOとGOSUBsを可能にGOTO 100 + A*10し、ZXスペクトラムマニュアルの付録CリストGO TO数式(定数に制限なし)を受け入れるように。ここだ議論計算のメリットのGOTOシンクレアZX80とZX81には。ところで、Spectrumバージョンでスペースが追加された理由はわかりません。
トビー・スペイト

回答:


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スタンド:)
デジタルトラウマ

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 for Applications、288バイト

BASICの方言で解決策を提供することは避けられませんでした。おそらく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

簡潔にするために、1文字の変数を多く使用しました。また、不要な空白はすべて抑制しました(VBEはインポート時にそれらを自動的に拡張します)。バイトカウントは、改行としてCHR(10)を使用した最終的な.BASファイル用です。

VBEイミディエイトウィンドウから呼び出すことができるサブルーチンは、Sinclair BASICプログラムを開き(最初のパラメーターはASCIIファイルへのパス-プログラムを含むCHR(10)を改行として)、行の番号を付け直し、Variant変数に結果を書き込みます(2番目のパラメーター)。

昇順、すべての可能なソース行番号に反復することで、それぞれのいずれかのアイデア、一致するすべての行番号を一度に交換するだけでなくGO TOGO SUBおよびRUN参照次の利用可能なターゲット行番号です。このアプローチを使用すると、いかなる種類の変換テーブルも必要ありません。ソース行番号が一致するたびにターゲット行番号が増分されるため、「誤った」行参照は次の有効な番号に自動的に調整されます。改行文字は行頭と行末のマーカーとして使用され、CHR(0)-印刷できないためプログラムで使用されることはありません-は、同じ行が何度も再番号付けされないようにするための一時的なマーカーとして使用されます。

いくつかの発言:

  • 簡潔にするために、ジャンプ文との一致には可能な限り小さい文字列を使用します。検索文字列で行末を使用すると、引用されたオカレンスまたはユーザー関数(Sinclairで常に括弧を使用する)が含まれるリスクはありません。コンストラクトのGO TOために、より大きな文字列が必要ですFOR ... TO(例:compare 50 FOR X=AGO TO 100および50 GO TO 100

  • このコードは、フォーム内のステートメントGO TO200(空白なし)をサポートしていませんが、ZXマニュアルでは、いくつかの例で有効なコードであると暗示されています(処理に数十バイト必要です)。

  • このコードは、プログラムの最初と最後に改行を追加します。最終的にこれをクリーンアップすることができました(さらに10バイト)が、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

私はあなたが正しいと思う...それを忘れていた
-dnep

1

ピップ -rn、63バイト

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

オンラインでお試しください!

説明

セットアップ

-rフラグは、ローカル変数の行のリストとしてSTDINおよび格納の全てを読み出します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を変換する場合、9未満の2つの行番号(1および4)があります。2* 10 + 10は30になります。3を変換する場合、3未満の行番号(1)は1つあります。 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 TOGO SUB、およびRUN

プログラムの残りは単一の式であり、 gは、2つの正規表現置換(ベクトル化、の各行に適用)を実行g

これが最初の代替品です。

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

正規表現はRUNGO SUBおよびGO TO、番号が続く、行の終わりまで続きます。これにより、文字列内で一致するRUNことも、行番号なしで一致することもなくなります。

キャプチャグループの順序は重要です。最初のグループは、コマンド(の取り込みRUNGO SUBまたはGO TO)。2番目のグループは、使用する場合、次のいずれかをキャプチャします。SUBまたはのかをTO。この部分をキャプチャする必要はありませんが、キャプチャしていないグループには余分なバイトが必要になります。次に、3番目のグループが行番号をキャプチャします。

置換にはコールバック関数を使用します。ピップでコールバック関数を使用すると、全体の試合は最初の引数であるa、と順番にキャプチャグループは、後続の引数はbcd、とe。したがって、最初のグループにコマンドがb入り、3番目のグループに行番号が入りdます。必要な変更は、Lispスタイルと呼ばれる変換関数に行番号を渡すことだけです(nd)。次にb、スペースとスペースを連結して返します。

2番目の置換:行番号

変換するのは、行頭の行番号だけです。

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

正規表現は、行頭の一連の数字と一致します。ここでも、コールバック関数を使用します。今回nは、一致全体(最初の引数a)が変換したい数値であるため、変換関数自体で十分です。

これはプログラムの最後の式であるため、Pipは結果を自動印刷します。-nフラグは、改行と結果リストを分離します。

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