Viết thông dịch viên cho 99


99

99 (phát âm là "chín mươi chín") là một ngôn ngữ lập trình bí truyền hoàn toàn mới (không bị nhầm lẫn với 99 , lưu ý chữ nghiêng). Nhiệm vụ của bạn trong thử thách này là viết một thông dịch viên cho 99 càng ngắn càng tốt. Việc gửi với ít byte nhất sẽ thắng. Tiebreaker đi đến trình được đăng đầu tiên.

Vì câu hỏi này sâu hơn một chút so với thông thường và tôi rất mong muốn được trả lời tốt, tôi sẽ trao phần thưởng 250 rep cho câu trả lời yêu thích của tôi (không nhất thiết phải là người chiến thắng).

99 Spec

99 là một ngôn ngữ bắt buộc . Mỗi dòng trong một chương trình 99 là một câu lệnh và trong khi thực hiện, con trỏ lệnh bắt đầu ở dòng trên cùng và đi qua từng dòng tiếp theo theo thứ tự, thực hiện chúng trên đường đi. Chương trình kết thúc khi dòng cuối cùng đã được thực thi. Các câu lệnh Goto có thể định tuyến lại đường dẫn của con trỏ lệnh.

Dòng mới, không gian và 9là ba ký tự duy nhất quan trọng trong chương trình 99 . Tất cả các nhân vật khác hoàn toàn bị bỏ qua. Ngoài ra, dấu cách trên mỗi dòng được bỏ qua và nhiều khoảng trắng trong một hàng được đọc thành một khoảng trắng. ("Dòng mới" đề cập đến bất kỳ mã hóa ngắt dòng phổ biến nào . Không có vấn đề gì mà trình thông dịch của bạn sử dụng.)

Vì vậy, chương trình này:

   9      BLAH        99   9a9bb9c9
9 this line and the next have 6 trailing spaces 9      
      

Đồng nhất với chương trình này:

 9 99 9999
9 9

Biến

Các biến trong 99 đều có tên là một hoặc nhiều 9chuỗi được nối với nhau ( 9+trong biểu thức chính quy). Ví dụ, 9, 99, và 9999999999là tất cả các biến rõ rệt. Đương nhiên, có vô số (hạn chế bộ nhớ).

Giá trị của mỗi biến là một số nguyên chính xác được ký, tùy ý . Theo mặc định, mỗi biến được gán cho biểu diễn số riêng của nó. Vì vậy, trừ khi nó đã được gán lại, giá trị của biến 9là số 9 và giá trị của biến 99là số 99, v.v. Bạn có thể nghĩ về nó như coi các biến là số đơn giản cho đến khi chúng được gán rõ ràng.

Tôi sẽ sử dụng Vđể chỉ một tên biến tùy ý bên dưới.
Mỗi thể hiện của Vcó thể được thay thế bằng 9, 99, 999, 9999vv

Các câu lệnh

Có năm loại tuyên bố khác nhau trong 99 . Mỗi dòng trong một chương trình 99 chứa chính xác một câu lệnh.

Cú pháp được mô tả ở đây giả sử tất cả các ký tự không liên quan đã bị xóa, tất cả các khoảng trắng ở cuối đã bị xóa và tất cả các chuỗi của nhiều khoảng trắng đã được thay thế bằng các khoảng trắng đơn.

1. Không hoạt động


Một dòng trống là không có op . Nó không làm gì cả (ngoài việc tăng con trỏ lệnh).

2. Đầu ra

V

Một biến duy nhất Vtrên một dòng in biến đó thành thiết bị xuất chuẩn.

Nếu Vcó một số lẻ của 9's ( 9, 999, vv) thì giá trị số nguyên của Vchia cho 9 sẽ được in (trong hệ thập phân).

Nếu Vcó số chẵn 9( 99, 9999v.v.) thì ký tự ASCII có mã Vchia cho 9, mod 128 sẽ được in. (Đó là (V / 9) % 128giá trị từ 0 đến 127.)

Ví dụ : Chương trình

9
9999

sẽ in 1W. Dòng đầu tiên in 1vì 9/9 là 1. Dòng thứ hai in Wvì 9999/9 là 1111 và 1111 mod 128 là 87 và 87 là mã ký tự cho W.

Lưu ý rằng ngắt dòng không được in giữa các mã thông báo đầu ra. \ncần phải được in rõ ràng để ngắt dòng.

3. Đầu vào

 V

Một biến duy nhất Vtrên một dòng có không gian hàng đầu lấy đầu vào từ stdin và lưu nó trong biến đó.

Nếu Vcó số lẻ 9thì người dùng có thể nhập bất kỳ số nguyên đã ký nào và Vsẽ được đặt thành 9 lần giá trị đó.

Nếu Vcó số chẵn 9thì người dùng có thể nhập bất kỳ ký tự ASCII nào và Vsẽ được đặt thành 9 lần mã ký tự của nó.

Ví dụ : Cho -57Alàm đầu vào, chương trình này

 9
9
 99
99

sẽ đầu ra -57A. Trong nội bộ, biến 9sẽ có giá trị -513 và 99sẽ có giá trị 585.

Thông dịch viên của bạn có thể cho rằng các đầu vào luôn có giá trị cú pháp.

4. Chuyển nhượng

Tuyên bố này có thể dài tùy ý. Đó là hai hoặc nhiều biến trên một dòng, cách nhau bởi khoảng trắng:

V1 V2 V3 V4 V5 ...

Điều này gán cho tổng của tất cả các chỉ số chẵn, trừ tổng của các chỉ số lẻ (không bao gồm ). Bài tập là theo giá trị, không phải bằng tham chiếu.V1VVV1

Nó có thể được dịch trong hầu hết các ngôn ngữ như .V1 = V2 - V3 + V4 - V5 + ...

Vì vậy, nếu chỉ có hai biến, thì đó là phép gán bình thường:

V1 V2V1 = V2

Nếu có ba, thì đó là phép trừ:

V1 V2 V3V1 = V2 - V3

Và dấu +/ -tiếp tục chuyển đổi qua lại với mỗi biến bổ sung:

V1 V2 V3 V4V1 = V2 - V3 + V4

Ví dụ : Chương trình này sẽ xuất ra 1110123:

999           Prints triple-nine divided by nine (111).
999 9 9       Assigns triple-nine to zero (nine minus nine).
999           Prints triple-nine divided by nine (0)
9 999 9       Assigns single-nine to negative nine (zero minus nine).
999 999 9     Adds nine to triple-nine (really subtracts negative nine).
999           Prints triple-nine divided by nine (1).
999 999 9     Adds nine to triple-nine (really subtracts negative nine).
999           Prints triple-nine divided by nine (2).
999 999 9     Adds nine to triple-nine (really subtracts negative nine).
999           Prints triple-nine divided by nine (3).

5. Goto (nhảy nếu tất cả bằng không)

Tuyên bố này cũng có thể dài tùy ý. Đó là hai hoặc nhiều biến trên một dòng, cách nhau bởi khoảng trắng, với khoảng trắng ở đầu :

 V1 V2 V3 V4 V5 ...

Nếu một số giá trị bên cạnh khác không, thì giá trị này hoạt động giống như một số không. Con trỏ lệnh được chuyển sang dòng tiếp theo như bình thường.V1

Nếu tất cả các giá trị bên cạnh không, sau đó con trỏ lệnh được chuyển đến dòng số . Các dòng được lập chỉ mục bằng 0, vì vậy nếu bằng 0, thì con trỏ di chuyển đến dòng trên cùng. Chương trình chấm dứt (thông thường, không có lỗi) nếu âm hoặc lớn hơn chỉ số cao nhất có thể (số dòng trừ một).V1 V1V1V1

Lưu ý rằng không được chia cho 9 ở đây. Và vì không thể có một biến là một giá trị không phải là bội của 9, nên chỉ các số dòng là bội số của 9 có thể được nhảy tới.V1

Ví dụ:

Chương trình này sẽ in 1mãi mãi:

9          Prints single-nine divided by nine (always 1).
99 9 9     Assigns double-nine to zero.
 99 99     Jumps to line zero (top line) if double-nine is zero.

Chương trình này

99999999                                              Print G.
999 99                                                Set triple-nine to ninety-nine.
9999999999 9999999999 9999999999 99 99 9 9 999 999    Set 10-nine to zero.
99999999999 9999999999                                Set 11-nine to zero.





999                                                   Print triple-nine's value divided by nine. (This is the ninth line.)
99999999                                              Print G.
999 999 9                                             Subtract nine from triple-nine.
 99999 999                                            Jump to line 5-nines if triple-nine is zero (ends program).
 9 99999999999 9999999999                             Jump to line nine if 10-nine and 11-nine are zero (always jumps).

sẽ xuất các số từ 11 xuống 1, theo thứ tự giảm dần, được bao quanh bởi G's:

G11G10G9G8G7G6G5G4G3G2G1G

Chi tiết bổ sung

Trình thông dịch lý tưởng sẽ chạy từ dòng lệnh với tên tệp chương trình 99 làm đối số. I / O cũng sẽ được thực hiện khi đang bay trong dòng lệnh.

Tuy nhiên, bạn có thể chỉ cần viết một hàm thông dịch có trong chương trình dưới dạng chuỗi cũng như danh sách các mã thông báo đầu vào (ví dụ ["-57", "A"]). Hàm sẽ in hoặc trả về chuỗi đầu ra.

Các cách khác nhau để chạy trình thông dịch và xử lý I / O đều ổn nếu các tùy chọn này là không thể trong ngôn ngữ của bạn.


Phần thưởng: Viết một cái gì đó tuyệt vời vào năm 99 và tôi sẽ vui lòng đưa nó vào bài viết này làm ví dụ.


Hy vọng bạn thích thử thách thứ 99 của tôi ! :CƯỜI MỞ MIỆNG


9
Tôi đã cân nhắc việc nâng cấp, nhưng điểm số hiện tại của bạn là 9 trận
wchargein

30
@WChargin có vẻ như bây giờ bạn sẽ phải cố gắng lấy 99.
trlkly

5
Chắc chắn có một phần thưởng cho tự lưu trữ (viết một 99 thông dịch viên ở 99 ), không có?
Gabe

5
@Gabe Một câu trả lời như thế có lẽ sẽ nhận được tiền thưởng, nhưng nếu đó là câu trả lời duy nhất, điều gì sẽ diễn giải người phiên dịch? ;)
Sở thích của Calvin

1
@Optimizer nó hoạt động: pastebin.com/raw.php?i=h73q58FN
coredump

Câu trả lời:


16

CJam, 157 byte

{:I;_N" 9"+--N/:P:,$W=){1a*Ab}%:V;{PT):T(=:LS%_{LS#\:,_,({(\{V=}%@{V-1@{2$*+0@-\}*\;t:V;}{:|T@V=9*?:T;}?}{~\{_V=\1&!{128%c}*o}{VIW):W=it:V;}?}?}R?Tg)TP,<*}g}

Dùng thử trực tuyến:

Giải trình

Cố gắng định dạng này với sự thụt lề thích hợp và các bình luận có thể sẽ mất mãi mãi, vì vậy tôi sẽ chỉ đưa ra một bản tóm tắt thuật toán.

Mã này là một khối, tương tự như chức năng ẩn danh của CJam. Khối dự kiến ​​chuỗi chương trình và danh sách các đầu vào trên ngăn xếp khi được thực thi.

Khởi tạo bao gồm ba bước. Đầu tiên, danh sách đầu vào được lưu. Sau đó, mọi ký tự trong chương trình không có ý nghĩa sẽ bị xóa và kết quả được chia thành một danh sách các dòng và được lưu. Cuối cùng, danh sách biến được khởi tạo. Danh sách này ánh xạ từng biến, được lập chỉ mục theo độ dài tên, đến giá trị của nó chia cho 9 (một biến không bao giờ có thể giữ một giá trị không phải là bội số của 9 và tất cả các hoạt động ngoại trừ lợi ích goto từ thay đổi này). Danh sách này được khởi tạo cho đến độ dài của dòng dài nhất, là giới hạn trên của tên biến đổi dài nhất hiện tại. Ngoài ra còn có một chút khởi tạo ngầm do các giá trị biến ban đầu: số dòng là 0 và chỉ số đầu vào là -1.

Trình thông dịch được triển khai như người ta mong đợi: một vòng lặp đọc dòng tiếp theo, tăng số dòng và thực thi dòng trong khi số dòng chỉ vào một dòng hiện có. Phân tích cú pháp dòng đầu tiên kiểm tra xem dòng đó có trống không, sau đó các nhánh dựa trên việc mức độ là 1 hay> 1, sau đó các nhánh dựa trên việc có không gian hàng đầu hay không. Bốn chi nhánh này mô phỏng bốn hoạt động (không bao gồm hoạt động không hoạt động) một cách đơn giản, mặc dù chơi golf căng thẳng như mọi thứ khác. Có lẽ một điều tối ưu hóa cần lưu ý là, vì một chuỗi đầu vào hợp lệ sẽ luôn tạo ra một phần tử loại được chương trình mong đợi, tôi đã bỏ qua các trường hợp riêng cho đầu vào dựa trên độ dài của tên biến. Nó chỉ đơn giản được giả định rằng phần tử đọc từ danh sách đầu vào là loại dự kiến.


15
+1. Khá ngắn. Bây giờ, bạn có thể viết một trình thông dịch CJam vào năm 99 không? ;-)
coredump

9
@coredump * rùng mình *
Runer112

Chết tiệt, tôi chỉ có thể nhận được 195 và sau đó tôi đã mất hy vọng và đã từ bỏ: P
Trình tối ưu hóa

Điều này không có modulo chính xác khi in các giá trị âm. Điều này có thể được sửa chữa bằng cách thay thế 128%bằng 128,=.
Martin Ender

26

Python 3, 421 414 410 404 388 395 401 byte

Chơi gôn

import sys,re
v,i,c,g,L={},0,[re.sub('( ?)[^9]+','\\1',l).rstrip().split(' ')for l in open(sys.argv[1])],lambda i:v.get(i,int(i)//9),len
while-1<i<L(c):
 d=c[i];l=L(d);e,*f=d;i+=1
 if l>1:
  x,*y=f
  if e:w=list(map(g,f));v[e]=sum(w[::2])-sum(w[1::2])
  elif l==2:j=input();v[x]=int(j)if L(x)%2 else ord(j)
  elif~-any(g(j)for j in y):i=g(x)*9
 elif e:w=g(e);print(w if L(e)%2 else chr(w%128),end='')

Ung dung:

import sys, re

# Intialise variable table.
vars_ = {}
get_var = lambda i: vars_.get(i, int(i)//9)

# Parse commands.
commands=[re.sub('( ?)[^9]+','\\1',l).rstrip().split(' ') for l in open(sys.argv[1])]

# Run until the current instruction index is out of bounds.
index=0
while 0 <= index < len(commands):
    # Get the current command and increment the index.
    command = commands[index]
    l = len(command)
    first = command[0]
    index += 1

    if l > 1:
        # Handle the "assignment" command.
        if first:
            operands = [get_var(i) for i in command[1:]]
            vars_[first] = sum(operands[0::2]) - sum(operands[1::2])
        # Handle the "input" command.
        elif l==2:
            inp = input()
            vars_[command[1]] = int(inp) if len(command[1]) % 2 else ord(inp)
        # Handle the "goto" command.
        elif not any(get_var(i) for i in command[2:]):
            index = get_var(command[1]) * 9
    # Handle the "output" command.
    elif first:
        val = get_var(first)
        print(val if len(first) % 2 else chr(val % 128),end='')

Khá nhiều chỉ là một thực hiện theo nghĩa đen của thông số kỹ thuật, chơi golf xa như tôi có thể nhận được nó.

Chạy từ dòng lệnh bằng cách cung cấp tệp mã nguồn 99 làm đối số duy nhất (ví dụ: ví dụ cuối cùng từ OP):

> python3 ninetynine.py countdown.txt
G11G10G9G8G7G6G5G4G3G2G1G
>

Là một phần thưởng bổ sung, đây là một triển khai (khá kém) của "99 chai" trong 99 : http://pastebin.com/nczmzkFs


1
@DLosc: liên quan đến điểm đầu tiên của bạn: Tôi cũng biết rằng elsesau khi một số có thể bị xóa, nhưng khi tôi thử nó trước đó tôi đã gặp lỗi cú pháp. Lời khuyên khác của bạn được nhiều đánh giá cao mặc dù!
Mac

3
@coredump: cách viết thông số kỹ thuật, mỗi biến sẽ luôn có một giá trị chia hết cho chín. Tôi thấy nó ngắn gọn hơn khi cho phép các biến nhận bất kỳ giá trị nào và chỉ nhân / chia cho chín khi cần thiết (đặc biệt, trong gotothường trình và khi nhận giá trị mặc định của biến). Đối với một người sử dụng ngôn ngữ có liên quan, nó không làm cho sự khác biệt.
Mac

2
Không phải elsechính nó, chỉ là không gian trước nó. Ví dụ 3*n+1if n%2else n//2.
DLosc

1
@DLosc: xin lỗi, tôi nhớ sai - Tôi thực sự có nghĩa là không gian, không phải là else. Ví dụ, tôi đã thử thay thế print(w if L(e)%2 else chr(w%128))bằng print(w if L(e)%2else chr(w%128))và có một ngoại lệ cú pháp.
Mac

1
lạ - Tôi đã thử nghiệm trên ideone.com và nó đã hoạt động, nhưng bạn nói đúng, nó không hoạt động trong trình thông dịch Python3 thực tế (3.4.0 trên Ubuntu). Bài đăng mẹo này làm rõ: một số được theo sau bởi mã thông báo theo thứ tự chữ cái hoạt động chung, nhưng không phải cho các mã thông báo bắt đầu bằng ehoặc E, và (từ các nhận xét) không dành cho 0ormột trong hai.
DLosc

16

Lisp thường gặp, 1180 857 837 836 byte

Tôi biết điều này sẽ không chiến thắng, nhưng tôi đã rất vui khi chơi gôn này. Tôi đã quản lý để loại bỏ 343 byte, tức là hơn hai 99 trình thông dịch được viết bằng CJam.

Ngoài ra, khá thú vị, tôi càng cố nén nó, tôi càng bị thuyết phục rằng đối với Common Lisp, việc biên dịch mã sẽ ngắn hơn so với việc cố gắng diễn giải nó một cách nhanh chóng.

(defmacro g(g &aux a(~ -1)> d x q(m 0)r v(n t)c(w 0)? u z)(flet((w(n p)(intern(format()"~a~a"p n))))(#1=tagbody %(case(setf c(ignore-errors(elt g(incf ~))))(#\  #2=(when(> w 0)(pushnew w v)(if u()(setq ?(oddp w)))(#5=push(w w'V)u)(setf w 0))(setf z t))(#\9(incf w)(setf >(or >(and n z))z()n()))((#\Newline())#2#(#5#(when u(setf u(reverse u)a(pop u))(if >(if u`(when(every'zerop(list,@u))(setf @,a)(go ^))`(setf,a,(if ?'(read)'(char-code(read-char)))))(if u`(setf,a,(do(p m)((not u)`(-(+,@p),@m))(#5#(pop u)p)(#5#(if u(pop u)0)m)))`(princ,(if ? a`(code-char(mod,a 128)))))))r)(incf m)(setf ?()u()z()>()n t)))(if c(go %))$(decf m)(setq d(pop r))(if d(#5# d x))(when(=(mod m 9)0)(#5#(w #3=(/ m 9)'L)x)(#5#`(,#3#(go,(w #3#'L)))q))(if(>= m 0)(go $)))`(let(@,@(mapcar(lambda(n)`(,(w n'V),(/(1-(expt 10 n))9)))v))(#1#,@x(go >)^(case @,@q)>))))
  • phân tích từ vựng và tạo mã được xen kẽ: Tôi không lưu trữ biểu diễn bên trong, nhưng xử lý trực tiếp từng dòng.
  • Có một vòng duy nhất tagbodyđể thực hiện 2 vòng lặp:

     (... (tagbody % ... (go %) $ ... (go $)) result)
    
  • biến cục bộ được khai báo trong &aux

  • không tạo ra một bao đóng, nhưng trực tiếp mã được giải thích
  • Vân vân.

Ungolfed, bình luận

(defmacro parse-99
    (string &aux
              (~ -1) ; current position in string
              a      ; first variable in a line 
              >      ; does current line starts with a leading space?
              d      ; holds a statement during code generation
              x      ; all statements (labels + expressions)
              q      ; all generated case statements 
              (m 0)  ; count program lines (first increases, then decreases) 
              r      ; list of parsed expressions (without labels)
              v      ; set of variables in program, as integers: 999 is 3
              (n t)  ; are we in a new line without having read a variable? 
              c      ; current char in string 
              (w 0)  ; currently parsed variable, as integer 
              ?      ; is first variable odd? 
              u      ; list of variables in current line, as integers
              z)     ; is the last read token a space?
  (flet((w(n p)
          ;; produce symbols for 99 variables
          ;; e.g. (10 'V) => 'V10
          ;;      (4 'L)  => 'L4
          (intern(format()"~a~a"p n))))
    (tagbody
     parse
       (case (setf c
                   ;; read current char in string,
                   ;; which can be NIL if out-of-bounds
                   (ignore-errors(aref string (incf ~))))

         ;; Space character
         (#\Space
          #2=(when(> w 0)
               (pushnew w v)            ; we were parsing a variable, add it to "v"
               (if u()(setq ?(oddp w))) ; if stack is empty, this is the first variable, determine if odd
               (push(w w'V)u)           ; add to stack of statement variable
               (setf w 0))              ; reset w for next variable

          ;; Space can either be significant (beginning of line,
          ;; preceding a variable), or not. We don't know yet.
          (setf z t))

         ;; Nine
         (#\9
          (incf w) ; increment count of nines
          (setf >(or >(and n z)) ; there is an indent if we were
                                 ; starting a newline and reading a
                                 ; space up to this variable (or if we
                                 ; already know that there is an
                                 ; indent in current line).
                ;; reset z and n
                z()n()))

         ;; Newline, or end of string
         ((#\Newline())
          #2#  ;; COPY-PASTE the above (when(> w 0)...) statement,
               ;; which adds previously read variable if necessary.

          ;; We can now convert the currently read line.
          ;; We push either NIL or a statement into variable R.

          (push(when u
                     (setf u (reverse u) ; we pushed, we must reverse
                           a (pop u))    ; a is the first element, u is popped
                     (if >
                         ;; STARTS WITH LEADING SPACE
                         (if u
                             ;; JUMP
                             `(when(every'zerop(list,@u))(setf @,a)(go ^))

                             ;; READ
                             `(setf,a,(if ?'(read)'(char-code(read-char)))))

                         ;; STARTS WITH VARIABLE
                         (if u

                             ;; ARITHMETIC
                             `(setf,a,(do(p m) ; declare p (plus) and m (minus) lists

                                         ;; stopping condition: u is empty
                                         ((not u)
                                          ;; returned value: (- (+ ....) ....)
                                          `(-(+,@p),@m))

                                        ;; alternatively push
                                        ;; variables in p and m, while
                                        ;; popping u

                                        (push(pop u)p)

                                        ;; first pop must succeed, but
                                        ;; not necessarly the second
                                        ;; one.  using a zero when u
                                        ;; is empty covers a lot of
                                        ;; corner cases.

                                        (push(if u (pop u) 0) m)))

                             ;; PRINT
                             `(princ,(if ? a`(code-char(mod,a 128)))))))
               r)
          ;; increase line count
          (incf m)
          ;; reset intermediate variables
          (setf ?()u()z()>()n t)))

       ;; loop until end of string
       (if c (go parse))


     build
       ;;; Now, we can add labels in generated code, for jumps

       ;; decrease line count M, which guards our second loop
       (decf m)

       ;; Take generated statement from R
       (setq d(pop r))

       ;; we pop from R and push in X, which means X will eventually
       ;; be in the correct sequence order. Here, we can safely
       ;; discard NIL statements.

       ;; We first push the expression, and THEN the label, so that
       ;; the label ends up being BEFORE the corresponding statement.
       (if d(push d x))

       ;; We can only jump into lines multiple of 9
       (when (=(mod m 9)0)
         ;; Push label
         (push(w #3=(/ m 9)'L)x)
         ;; Also, build a case statement for the jump table (e.g. 2(go L2))
         (push`(,#3#(go,(w #3#'L)))q))
       ;; loop
       (if(>= m 0)(go build)))

    ;; Finally, return the code
    `(let(@ ; target of a jump instruction

          ;; other variables: V3 represents 999 and has a default value of 111
          ,@(mapcar(lambda(n)`(,(w n'V),(/(1-(expt 10 n))9)))v))

       ;; build a tagbody, inject statements from X and case statements from Q
       ;; label ^ points to jump table : we go to ^ each time there is a JUMP
       ;; label > is the end of program

       ;; note that if the case does not match any authorized target
       ;; address, we simply end the programs.
       (tagbody,@x(go >)^(case @,@q)>))))

Chúng tôi sử dụng đầu vào / đầu ra tiêu chuẩn trong quá trình đánh giá, nghĩa là chúng tôi sử dụng các tiêu chuẩn readprincchức năng. Do đó, mã kết quả có thể được thực thi trên dòng lệnh, như hiển thị bên dưới.

Các đầu vào không được vệ sinh hoàn toàn tốt khi chạy 99 chương trình: người dùng cho rằng người dùng biết loại giá trị nào được mong đợi.

Chi phí thời gian chạy duy nhất có thể có thể xảy ra khi nhảy, vì chúng ta phải đánh giá giá trị của một biến và khớp giá trị đó với nhãn. Ngoại trừ điều đó, thông dịch viên sẽ khá hiệu quả.

Dựa trên sự quan sát thông minh từ Mac mà chúng ta không cần chia và nhân cho 9 lần, phiên bản hiện tại quản lý để không bao giờ chia hoặc nhân với 9 trong khi thực hiện.

Thí dụ

Nếu chúng ta thay thế defmacrobằng defun, chúng ta sẽ thấy mã được tạo. Ví dụ:

(g
"99999999                                              Print G.
999 99                                                Set triple-nine to ninety-nine.
9999999999 9999999999 9999999999 99 99 9 9 999 999    Set 10-nine to zero.
99999999999 9999999999                                Set 11-nine to zero.





999                                                   Print triple-nine's value divided by nine. (This is the ninth line.)
99999999                                              Print G.
999 999 9                                             Subtract nine from triple-nine.
 99999 999                                            Jump to line 5-nines if triple-nine is zero (endsprogram).
 9 99999999999 9999999999                             Jump to line nine if 10-nine and 11-nine are zero (alwa

")

Đây là mã kết quả:

(LET (@
      (V5 11111)
      (V11 11111111111)
      (V1 1)
      (V10 1111111111)
      (V2 11)
      (V3 111)
      (V8 11111111))
  (TAGBODY
   L0
    (PRINC (CODE-CHAR (MOD V8 128)))
    (SETF V3 (- (+ V2) 0))
    (SETF V10 (- (+ V3 V1 V2 V10) V3 V1 V2 V10))
    (SETF V11 (- (+ V10) 0))
   L1
    (PRINC V3)
    (PRINC (CODE-CHAR (MOD V8 128)))
    (SETF V3 (- (+ V3) V1))
    (WHEN (EVERY 'ZEROP (LIST V3)) (SETF @ V5) (GO ^))
    (WHEN (EVERY 'ZEROP (LIST V11 V10)) (SETF @ V1) (GO ^))
    (GO >)
   ^
    (CASE @ (0 (GO L0)) (1 (GO L1)))
   >))

Khi được thực thi, in "G11G10G9G8G7G6G5G4G3G2G1G"

Dòng lệnh

Chúng ta có thể xây dựng một tệp thực thi bằng cách bỏ lõi và chỉ định toplevelhàm. Xác định một tệp có tên boot.lispnơi bạn đặt defmacro, và sau đó viết như sau:

(defun main()(parse-99 <PROGRAM>))
(save-lisp-and-die "test-99" :executable t :toplevel #'main)

Chạy sbcl --load boot.lispcho đầu ra sau:

$ sbcl --load boot.lisp 
This is SBCL 1.2.8.32-18c2392, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
[undoing binding stack and other enclosing state... done]
[saving current Lisp image into test-99:
writing 5824 bytes from the read-only space at 0x20000000
writing 3120 bytes from the static space at 0x20100000
writing 55771136 bytes from the dynamic space at 0x1000000000
done]

Sau đó, chạy chương trình biên dịch 99 :

$ time ./test-99
G11G10G9G8G7G6G5G4G3G2G1G
real    0m0.009s
user    0m0.008s
sys     0m0.000s

99 chai

Nếu bạn quan tâm, đây là mã được biên dịch cho chương trình 99 chai được viết bằng câu trả lời của Mac : http://pastebin.com/ZXe839CZ (đây là phiên bản cũ nơi chúng tôi có jmpendnhãn, số lambda xung quanh và số học đẹp hơn).

Đây là một thực thi với phiên bản mới, để chứng minh rằng nó vẫn hoạt động: http://pastebin.com/raw.php?i=h73q58FN


6

TI-84 Basic (Tập lệnh Máy tính), 376 373 377 381 byte

Nếu nó chạy trên máy tính TI-84, bạn sẽ có thể sử dụng nó trong bài kiểm tra tiêu chuẩn hóa ... vì vậy nó rất hữu ích;)

Phiên bản hệ điều hành tối thiểu - 2.53MP (MathPrint) do sigma tổng kết

#Get input from STDIN
:Ans+":"->Str0
#Initialize instruction pointer
:1->I
#Initialize variable set
:DelVar L1999->dim(L1
#Strip out those pesky non-newline/space/9 characters
:For(J,1,length(Ans
:sub(Str0,J,1
:If not(inString(": 9",Ans
:sub(Str0,1,J-1)+sub(Str0,J+1,length(Str0)-J->Str0
:End
#Main interpreting loop
:While I<length(Str0
:sub(Str0,I+1,inString(Str0,":",I+1)-I-1->Str1
:DelVar A" "=sub(Ans,1,1->A
:inString(Str0,":",I+1->I
:If A
:sub(Str1,2,length(Str1)-1->Str1
:End
:length(Str1->L
#0 is Output, 1 is Input, 2 is Assignment, 3 is Goto
:2A+inString(Str1," ->B
:If not(Ans
:Disp L1(L
:If Ans=1
:Then
:Input C
:C->L1(L
:End
#Get those delimited variables
:If B>1
:Then
:"{"+Str1->Str2
:While inString(Ans," 
:inString(Ans," 
:sub(Str2,1,Ans-1)+sub(Str2,Ans+1,length(Str2)-Ans->Str2
:End
:log(expr(Ans)+1->L2
:End
:If B=2
#Gotta expand that -+ pattern
:Ans(2->L1(Ans(1
;Love that summation Σ
:If B=3 and Σ(L2(K),K,2,dim(L2
:Then
:DelVar IFor(K,0,9L2(1
:inString(Str0,":",I+1->I
:End
:End

Hướng dẫn PS ASCII không thể được tuân thủ chính xác, nhưng trong TI-Basic :là một dòng mới. Do đó, tất cả các dòng mới thực tế trong mã có nghĩa là :hoặc #ở đầu mỗi dòng không bắt buộc. Các mã thông báo bắt đầu :#chỉ phân biệt giữa các nhận xét và mã.

Kết xuất Hex gốc (376 byte)

49 3f bb 54 5d 20 39 39 39 04 b5 5d 20 3f 72 04 aa 09 3f d3 4a 2b 31 2b bb 2b 72 3f bb 0c aa 09 2b 4a 2b 31 3f ce b8 bb 0f 2a 3e 29 39 2a 2b 72 3f bb 0c aa 09 2b 31 2b 4a 71 31 11 70 bb 0c aa 09 2b 4a 70 31 2b 72 71 4a 04 aa 09 3f d4 3f d1 49 6b bb 2b aa 09 3f bb 0c aa 09 2b 49 70 31 2b bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 11 71 49 71 31 04 aa 20 3f bb 54 41 2a 29 2a 6a bb 0c 72 2b 31 2b 31 04 41 3f bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 04 49 3f ce 41 3f bb 0c aa 20 2b 32 2b bb 2b aa 20 11 71 31 04 aa 20 3f d4 3f bb 2b aa 20 04 4c 3f 32 41 70 bb 0f aa 20 2b 2a 29 04 42 3f ce b8 72 3f de 5d 20 10 4c 11 83 39 3f ce 72 6a 31 3f cf 3f dc 43 3f 39 43 04 5d 20 10 4c 3f d4 3f ce 42 6c 31 3f cf 3f 2a 08 2a 70 aa 20 04 aa 01 3f d1 bb 0f 72 2b 2a 29 3f bb 0f 72 2b 2a 29 3f bb 0c aa 01 2b 31 2b 72 71 31 11 70 bb 0c aa 01 2b 72 70 31 2b bb 2b aa 01 11 71 72 04 aa 01 3f d4 3f c0 bb 2a 72 11 70 31 04 5d 01 3f d4 3f ce 42 6a 32 3f 72 10 32 04 5d 20 10 72 10 31 3f ce 42 6a 33 40 ef 33 5d 01 10 4b 11 2b 4b 2b 32 2b b5 5d 01 3f cf 3f bb 54 49 d3 4b 2b 30 2b 5d 01 10 31 3f bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 04 49 3f d4 3f d4 2e 76

Chỉnh sửa # 1 - Tối ưu hóa 3 byte bằng cách sử dụng quan sát của Mac Chỉnh sửa # 2 & # 3 - Đã sửa lỗi phát hiện bởi Runer112.


11
Dễ sử dụng trong các tình huống căng thẳng như các bài kiểm tra tiêu chuẩn là chính xác những gì tôi thiết kế 99 cho.
Sở thích của Calvin

1
Tôi có thể đề xuất sử dụng một ký tự khác, như #, cho các bình luận không? (NB: Nhận xét trong mã thực tế được triển khai dưới dạng một dòng chỉ có một chuỗi không được tiết lộ, làm
tắc nghẽn

8
Bạn đã thực sự thử chạy cái này chưa? Tôi không có, nhưng chỉ cần nhìn vào nó thêm một chút, tôi đã phát hiện ra những gì dường như là ít nhất là một nửa tá lỗi. Ví dụ: các biến không được khởi tạo với các giá trị của chúng, Ansđầu vào bị ghi đè nên Ans->Str0trên dòng 6 sẽ có lỗi, có nhiều trường hợp trong đó đối số độ dài của sub()lệnh có thể bằng 0 dẫn đến lỗi, Anstrên dòng 11 sẽ là một chuỗi vì vậy Ans-Jsẽ có lỗi ... Và tôi chỉ nhìn vào khoảng nửa đầu của chương trình.
Runer112

1
@Timtech Điều đó vẫn để lại các vấn đề khác, mặc dù. Như tôi đã đề cập, thiếu ký tự I / O, thiếu khởi tạo biến và nhiều trường hợp trong đó một sub()lệnh có thể có độ dài bằng 0 và gây ra lỗi. Và một khi các sub()yêu cầu đã được sửa, tôi sợ nó có thể tiết lộ nhiều vấn đề hơn.
Runer112

1
@Timtech Tôi đã đề cập đến điều này: "Theo mặc định, mỗi biến được gán cho biểu diễn số của chính nó. Vì vậy, trừ khi nó được gán lại, giá trị của biến 9là số 9 và giá trị của biến 99là số 99, và v.v. " Và các chuỗi có độ dài 0 có thể được tạo ra bằng các phương tiện như "", nhưng đó là một lỗi mà về cơ bản không có lệnh thao tác chuỗi nào có thể tiêu thụ hoặc tạo ra một chuỗi rỗng, bao gồm cả sub().
Runer112

5

C 426 458 481 497

Chỉnh sửa Có thể tôi đang đi quá xa, nhưng điều này hoạt động với Visual C: đã xóa stdio.h, sử dụng int thay vì FILE * cho fopen và getc

Chỉnh sửa 2 Sắp xếp lại bước thực hiện, lộn xộn hơn, lưu 32 ký tự

B[99999],*r,*i[9999],V[999],v,w,m,n;unsigned p,s;
main(b,a)char*a[];{r=i[0]=B;m=fopen(a[1],"r");
do if(w=getc(m),n+=w==57,w<33){
if(n){for(v=1,b=n;--b;)v=v*10+1;V[n]=v;*r++=p?-n:n;b=n=0;};
w-32?(*r=p=0,b=i[++s]=++r):(p=b,b=0);}while(w>=0);
while(p<s)if(w=0,r=i[p++],v=*r++)
if(m=v>0,*r){for(;b=*r++;m=-m)w=w+m*V[b]|!m*V[b];m?V[v]=w:(p=w?p:9*V[-v]);
}else~v&1?!m?V[-v]=getchar():putchar(V[v]&127):m?printf("%d",V[v]):scanf("%d",V-v);
}

Chương trình điều khiển độc lập, tên chương trình được thực hiện trên dòng lệnh và đầu vào / đầu ra thông qua bàn điều khiển.

K & R kiểu cũ, kiểu int mặc định cho các thông số và thông số toàn cầu. Giả sử EOF được định nghĩa là -1 (như trong mọi triển khai C tôi biết)

Biên dịch với các cảnh báo với Visual Studio 2010 (dự án C ++ của bảng điều khiển Win32, biên dịch thành C) Biên dịch trên Ideone, nhưng không thể chạy vì nó cần một tệp.

Bước đầu tiên, mã nguồn được đọc và phân tích cú pháp, mỗi dòng được lưu trữ dưới dạng một chuỗi các số nguyên dựa trên số 9s. Nếu có một khoảng trống hàng đầu, số đầu tiên là âm. Vì vậy: 9 BLAH 99 9a9bb9c9( 9 99 9999) trở thành -1,2,4 Có một phím tắt - không hợp pháp: tất cả các mã ascii nhỏ hơn '' đều được coi là dòng mới.

Trong bước này, tất cả các biến được sử dụng đều được xác định trước.

Bước thực hiện tuân theo các thông số kỹ thuật, không rườm rà, lưu số lưu trữ chia cho 9.

dễ đọc hơn (tôi hy vọng), không gian và dòng mới được thêm vào

B[99999],*r,*i[9999],V[999],v,w,m,n;
unsigned p,s;
main(b,a)char*a[];
{
  r=i[0]=B;
  m=fopen(a[1],"r");
  do if(w=getc(m),n+=w==57,w<33)
  {
     if(n){for(v=1,b=n;--b;)v=v*10+1;V[n]=v;*r++=p?-n:n;b=n=0;};
     w-32?(*r=p=0,b=i[++s]=++r):(p=b,b=0);
  }
  while (w>=0);
  while (p<s)
    if (w = 0, r = i[p++], v = *r++)
        if (m = v > 0, *r){
            for(; b = *r++; m = -m)
                w = w + m*V[b] | !m*V[b];
            m ? V[v]=w : (p = w ? p : 9*V[-v]);
        } else
            ~v & 1 
            ? !m ? V[-v] = getchar() : putchar(V[v] & 127)  
            : m ? printf("%d", V[v]) : scanf("%d", V - v);
}

1
Cũng hoạt động với GCC 4.8.2. Biên dịch thành C99!
EMBLEM

4

Haskell, 550 byte

import Data.List.Split
import System.Environment
a#b=takeWhile(/=a)b
(!)=map
main=do(f:_)<-getArgs;readFile f>>=e.(p!).lines
p l=(if ' '#l<'9'#l then[0]else[])++length!(wordsBy(/='9')l)
e l=(\x->div(10^x-1)9)%l where
 _%[]=return()
 v%([]:r)=v%r
 v%([n]:r)=putStr(if odd n then show(v n)else[toEnum$v n`mod`128])>>v%r
 v%([0,n]:r)=do i<-getLine;u n(if odd n then read i else fromEnum$head i)v%r
 v%((0:n:m):r)|any(/=0)(v!m)=v%r|v n<0=v%[]|1<2=v%drop(9*v n)l
 v%((n:m):r)=u n(sum$zipWith(*)(v!m)(cycle[1,-1]))v%r
u n i v= \x->if x==n then i else v x

Ví dụ chạy với chương trình "đếm ngược" được lưu trữ trong tệp i.99

$ ./99 i.99
G11G10G9G8G7G6G5G4G3G2G1G

Phiên bản bị đánh cắp:

import Data.List.Split
import System.Environment

-- The main function takes the first command line argument as a file name,
-- reads the content, splits it into lines, parses each line and evaluates
-- the list of parsed lines.
main = do
 (f:_)<-getArgs
 readFile f >>= eval.map parse.lines

-- each line is coverted into a list of integers, which represent the number
-- of 9s (e.g. "999 99 9999" -> [3,2,4]). If there's a space before the first
-- 9, a 0 is put in front of the list (e.g. " 9 9 999" -> [0,1,1,3]).
parse l = (if takeWhile (/=' ') l < takeWhile (/='9') l then [0] else [])
   ++ map length (wordsBy(/='9') l)

-- The work is done by the helper function 'go', which takes two arguments
--   a) a functions which takes an integer i and returns the value of the
--      variable with i 9s (e.g: input: 4, output: value of 9999). To be
--      exact, the value divided by 9 is returned.
--   b) a list of lines to work on
-- 'eval' starts the process with a function that returns i 1s for every i and
-- the list of the parsed input. 'go' checks which statement has to be
-- executed for the next line and calls itself recursively
eval list = go (\x -> div (10^x-1) 9) list
   where
   go _ []                  = return ()
   go v ([]:r)              = go v r
   go v ([n]:r)             = putStr (if odd n then show(v n) else [toEnum (v n`mod`128)]) >> go v r
   go v ([0,n]:r)           = do i<-getLine ; go (update n (if odd n then read i else fromEnum$head i) v) r
   go v ((0:n:m):r)
      | any (/=0) (map v m) = go v r
      | v n < 0             = go v []
      | otherwise           = go v (drop (9*v n) list)
   go v ((n:m):r)           = go (update n (sum $ zipWith (*) (map v m) (cycle[1,-1])) v) r

-- updates a function for retrieving variable values.
-- n = position to update
-- i = new value
-- v = the function to update
update n i v = \x->if x==n then i else v x

4

JavaScript (ES6) 340 352

Một hàm có 2 tham số

  • mã chương trình dưới dạng một chuỗi nhiều dòng
  • đầu vào như một mảng

Tham số tùy chọn thứ ba (10k mặc định) là số lần lặp tối đa - Tôi không thích một chương trình chạy mãi mãi

JSFiddle Để kiểm tra

I=(c,i,k=1e5,
  V=v=>v in V?V[v]:v/9 // variable getter with default initial value
)=>(c=>{
 for(p=o='';--k&&p<c[L='length'];)
   (v=(r=c[p++].split(' '))[S='shift']())? // no leading space
      r[r.map(t=>w-=(m=-m)*V(t),w=0,m=1),0]?V[v]=w // Assign
      :o+=v[L]&1?V(v):String.fromCharCode(V(v)&127) // Output
   : // else, leading space
    (v=r[S]())&&
       (r[0]?r.some(t=>V(t))?0:p=9*V(v) // Goto
       :(t=i[S](),V[v]=v[L]&1?t:t.charCodeAt()) // Input
    )
})(c.replace(/ (?=[^9])|[^9\s]/g,'').split('\n'))  // code cleaning
||o

4

q / k, 490 469

M:mod;T:trim;R:read0;S:set;s:" "
f:(rtrim')(f:R -1!`$.z.x 0)inter\:"9 \n"
k)m:{@[x;&M[!#x;2];-:]}
b:{}
k)p:{1@$$[1=M[#x;2];(K x)%9;"c"$M[(K x)%9;128]];}
k)i:{S[(`$T x);$[1=M[#T x;2];9*"J"$R 0;*9*"i"$R 0]]}
k)K:{$[#!:a:`$x;.:a;"I"$x]}
k)v:{(S).(`$*:;+/m@K'1_)@\:T's\:x}
k)g:{$[&/0=C:K'c:1_J:s\:T x;n::-1+K@*J;|/~0=C;;(d<0)|(d:*C)<#f;exit 0]}
k)r:{`b`p`i`v`g@*&(&/x=s;q&1=c;(e~s)&1=C;(q:e~"9")&1<c:#s\:x;((e:*x)~s)&1<C:#s\:1_x)}
k)n:0;while[~n>#o:(r')f;(o n)f n;n+:1]
\\

.

$ q 99.q countdown.txt -q
G11G10G9G8G7G6G5G4G3G2G1G

Kịch bản lệnh là hỗn hợp của q và k, vì vậy trước tiên tôi xác định một vài từ khóa q mà tôi muốn sử dụng nhiều lần trong các hàm k. (về cơ bản là macro #define)

M:mod;T:trim;R:read0;S:set

f đọc tệp được truyền vào chương trình và loại bỏ các ký tự không cần thiết

q)f
"99999999"
"999 99"
"9999999999 9999999999 9999999999 99 99 9 9 999 999"
"99999999999 9999999999"
""
""
""
""
""
"999"
"99999999"
"999 999 9"
" 99999 999"
" 9 99999999999 9999999999"

m lấy một danh sách / vectơ và nhân các chỉ số lẻ bằng -1

q)m 1 2 3 4 5
1 -2 3 -4 5

b chỉ là một hàm trống, được sử dụng cho các dòng không op

p là chức năng in.

Klà một chức năng kiểm tra một biến. Nếu biến tồn tại, nó trả về nó, nếu không nó chỉ trả về nghĩa đen.

//999 not defined, so just return 999
q)K "999"
999
//Set 999 to 9
q)v "999 9"
//K now returns 9
q)K "999"
9

v là hàm gán.

g là hàm goto.

r lấy một chuỗi và quyết định thao tác nào cần được áp dụng.

Và cuối cùng, tôi chỉ lặp qua fdanh sách các chuỗi, với ntư cách là trình lặp. Chức năng goto sẽ cập nhật nkhi cần thiết.


3

Perl, 273 266 255 244 238

Ngắt dòng thêm cho rõ ràng.

open A,pop;
for(@c=<A>){
y/ 9//cd;s/ +/ /g;s/ $//;
$p="((99)+|9+)";$a='+';
s/^ $p$/$1='$2'?ord<>:<>/;
s/^$p$/print'$2'?chr$1%128:$1/;
s/^ $p /\$_=$1*011unless/&&y/ /|/;
s/ /=/;s/ /$a=-$a/ge;
s!9+!${x.$&}=$&/9;"\$x$&"!eg}
eval$c[$_++]until/-/|$_>@c

Tên chương trình được thực hiện trên dòng lệnh:

$ perl 99.pl 99beers.99

Mỗi dòng của chương trình được chuyển đổi thành mã Perl, ví dụ:

print'$x99'?chr$x99999999%128:$x99999999
$x999=$x99
$x9999999999=$x9999999999-$x9999999999+$x99-$x99+$x9-$x9+$x999-$x999
$x99999999999=$x9999999999





print''?chr$x999%128:$x999
print'$x99'?chr$x99999999%128:$x99999999
$x999=$x999-$x9
$_=$x99999*011unless$x999
$_=$x9*011unless$x99999999999|$x9999999999

Thêm chi tiết

open A,pop; # open the source file
for(@c=<A>){ # read all lines into @c and iterate over them
y/ 9//cd; # remove all but spaces and 9's
s/ +/ /g;s/ $//; # remove duplicate and trailing spaces
$p="((99)+|9+)";$a='+';
s/^ $p$/$1='$2'?ord<>:<>/; # convert input
s/^$p$/print'$2'?chr$1%128:$1/; # convert output
s/^ $p /\$_=$1*011unless/&&y/ /|/; # convert goto
s/ /=/;s/ /$a=-$a/ge; # convert assignment
s!9+!${x.$&}=$&/9;"\$x$&"!eg} # initialize and convert variables
eval$c[$_++]until/-/|$_>@c # run (program counter is in $_)
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.