후퇴 금요일 : ZX 스펙트럼 기본 목록의 번호를 다시 매 깁니다.


15

제가 처음 접한 프로그래밍 언어는 Sinclair BASIC 입니다. 많은 BASIC 방언과 마찬가지로 모든 소스 코드 행에 번호를 매겨 야 합니다.

결과적 GO TO으로이 명령의 사용은 관용적이며 지정된 줄 번호 (라벨 없음)로 실행을 이동합니다.

또한 GO SUB기본적인 함수 호출로 사용할 수 있는 관련 명령이 있습니다. 다시 한 번 실행은 주어진 행 번호로 이동하지만 RETURN명령에 도달하면 실행은 다음에 나오는 다음 명령으로 다시 이동합니다 GO SUB.

마찬가지로 RUN명령은 주어진 행에서 프로그램 실행을 다시 시작합니다.

줄 번호가 매겨진 BASIC 통역사에서 시간을 보낸 사람은 번호가 매겨진 번호 매기기 체계를 사용하는 법을 배웠을 것입니다. 따라서 새로운 코드 줄을 쉽게 삽입 할 수 있습니다. 그러나 그럼에도 불구하고 계속 번호가 매겨진 줄 사이에 새 줄을 삽입해야 할 수도 있습니다.


행 번호가 지정된 BASIC 목록을 입력으로 지정하면 동일한 프로그램을 출력하지만 행 번호가 10에서 시작하여 10 씩 단계적으로 증가하도록 번호를 다시 매겼습니다. 입력 목록에 명령 GO TO또는 GO SUB명령 이있을 수 있으므로 이와 관련된 숫자도 조정해야합니다.

  • GO TOGO 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
향수 시간 - 내 첫 PC는 스펙트럼 48K와 처음으로 조립 프로그램 중 하나는 renumberer이었다
edc65

2
@ edc65 여전히 번호 재 조립 어셈블리 코드가 있습니까? 그렇다면 답변으로 게시 할 수 있습니다.
Digital Trauma

1
테스트 케이스는 문자열 리터럴에 적어도 하나의 goto / gosub를 포함해야합니다.
피터 테일러

1
: I는 언급 발견 ZX81 있음과 같이 계산 GOTOs GOSUBs 허용GOTO 100 + A*10 하고 ZX 스펙트럼 매뉴얼의 부록 C 리스트 GO TO숫자 식 (상수 제한 없음)를 받아들이고있다. 다음 은 ZX80 및 ZX81 에서 계산 된 장점에 대한 설명 입니다 GOTO. BTW, 왜 공간이 스펙트럼 버전에 추가되었는지 모르겠습니다.
Toby Speight

답변:


5

자바 스크립트 (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

펄 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

간결성을 위해 많은 한 글자 변수를 사용했습니다. 또한 불필요한 공백을 모두 억제했습니다 (VBE는 가져올 때 자동으로 확장합니다). 바이트 수는 CHR (10)을 줄 바꿈으로 사용하는 최종 .BAS 파일입니다.

VBE 즉시 실행 창에서 호출 할 수있는 서브 루틴은 Sinclair BASIC 프로그램을 열고 (첫 번째 매개 변수는 ASCII 파일의 경로-프로그램을 포함하는 CHR (10)-프로그램을 포함)-행 번호를 변경하고 Variant 변수에 결과를 씁니다. (두 번째 매개 변수).

아이디어는 오름차순, 그리고 각각에 대해,뿐만 아니라 모든 일치하는 행 번호를 한 번에 교체 가능한 모든 소스 라인의 번호를 반복하는 것입니다 GO TO, GO SUB그리고 RUN다음 사용 가능한 대상 행 번호와 참조. 이 방법을 사용하면 어떤 종류의 변환 표도 필요하지 않습니다. 소스 라인 번호에서 일치하는 것을 찾을 때마다 목표 라인 번호가 증가하므로 "잘못된"라인 참조는 다음 유효한 번호로 자동 조정됩니다. 줄 바꿈 문자는 시작 및 끝 라인의 마커로 사용되며 CHR (0)은 인쇄 할 수 없으므로 프로그램에서 절대 사용되지 않습니다. 같은 줄의 번호를 여러 번 다시 매길 수 없도록 임시 마커로 사용됩니다.

일부 비고 :

  • 간결성을 위해 점프 문과 일치시키기 위해 가능한 작은 문자열을 사용합니다. 검색 문자열에 줄 끝을 사용하면 인용 된 발생 또는 사용자 기능 (Sinclair에서 항상 괄호를 사용함)을 포함 할 위험이 없습니다. 구성 GO TO때문에 더 큰 문자열이 필요합니다 FOR ... TO(예 : 비교 50 FOR X=AGO TO 10050 GO TO 100)

  • GO TO200ZX 매뉴얼은 여러 예제에서 유효한 코드임을 암시하지만 코드는 공백이없는 형식의 명령문을 지원하지 않습니다 (이를 처리하는 데 12 바이트가 더 소요됨).

  • 이 코드는 프로그램의 시작 부분에 줄 바꿈을 추가하고 프로그램 끝에 줄 바꿈을 추가합니다. 나는 이것을 결국 (12 바이트 더) 정리할 수 있지만 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에는 내가 기억하는 한 문자열 대체 기능이 내장되어 있지 않기 때문에 BTW, QBasic 솔루션은 훨씬 더 길 것입니다.
DLosc

당신이 옳은 것 같아요 ... 그것에 대해 잊어 버렸습니다
dnep

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를 변환 할 때 9보다 작은 두 줄 번호 (1 및 4)가 있습니다. 2 * 10 + 10은 30을 제공합니다. 3을 변환 할 때 3보다 작은 한 줄 번호 (1)가 있습니다. 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 SUBGO TO, 숫자, 행 끝 중 하나와 일치합니다. 이렇게하면 문자열 내부와 일치 RUN하지 않으며 줄 번호없이 일치 하지 않습니다 .

캡처 그룹의 순서가 중요합니다. 첫 번째 그룹은 명령 (하나 포착 RUN, GO SUB또는을 GO TO). 두 번째 그룹은, 경우, 캡처를 사용하거나 SUB또는 TO. 이 부분을 캡처 할 필요는 없지만 캡처하지 않은 그룹에는 추가 바이트가 필요합니다. 그런 다음 세 번째 그룹은 줄 번호를 캡처합니다.

교체를 위해 콜백 함수를 사용합니다. 핍의 콜백 기능으로, 전체 경기는 첫 번째 인수 인 a및 주문의 캡처 그룹은 이후 인수입니다 b, c, d,와 e. 따라서 첫 번째 그룹에는 명령이 있고 세 번째 그룹 b에는 줄 번호가 있습니다 d. 우리가 변경해야 할 유일한 것은 Lisp-style :이라는 변환 함수를 통해 줄 번호를 전달하는 것 (nd)입니다. 그런 다음 b공백과 함께 연결 하여 반환합니다.

두 번째 교체 : 줄 번호

변환해야 할 것은 줄의 시작 부분에있는 줄 번호입니다.

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

정규식은 줄의 시작 부분에서 일련의 숫자와 일치합니다. 다시 우리는 콜백 함수를 사용합니다. 이번에 n는 전체 일치 (첫 번째 인수 a)가 변환 할 숫자 이므로 변환 함수 자체로 충분 합니다.

이것이 프로그램의 마지막 표현이므로 Pip은 결과를 자동으로 인쇄합니다. -n플래그는 줄 바꿈와 결과 목록을 구분합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.