Dịch mở đầu cho Befunge


19

Đây là Thử thách hàng tuần # 2. Chủ đề: Dịch

Viết chương trình hoặc hàm lấy mã nguồn cho chương trình trong Prelude và xuất mã cho chương trình tương đương trong Befunge-93 . Để chương trình tương đương, đối với bất kỳ đầu vào cụ thể nào, sẽ tạo ra cùng một đầu ra như chương trình Prelude và tạm dừng khi và chỉ khi chương trình Prelude tạm dừng.

Ngôn ngữ nhập: Mở đầu

Trình thông dịch Python:

Một chương trình Prelude bao gồm một số "tiếng nói" thực hiện các hướng dẫn đồng thời. Các hướng dẫn cho mỗi giọng nói là trên một dòng riêng biệt. Mỗi giọng nói có một ngăn xếp riêng, được khởi tạo với số lượng không giới hạn. Thực thi bắt đầu ở cột ngoài cùng bên trái và tiến một cột sang phải mỗi dấu, trừ khi bị ảnh hưởng bởi )hoặc (chỉ dẫn. Chương trình chấm dứt khi đạt đến cột cuối cùng.

Thông số mở đầu cho thử thách này:

Digits 0-9      Push onto the stack a number from 0 to 9. Only single-digit
                    numeric literals can be used.
^               Push onto the stack the top value of the stack of the above 
                    voice.
v               Push onto the stack the top value of the stack of the below 
                    voice.
#               Remove the top value from the stack.
+               Pop the top two integers from the stack and push their sum.
-               Pop the top two integers from the stack, subtract the topmost 
                    from the second, and push the result.
(               If the top of the stack is 0, jump to the column after the 
                    matching `)` after the current column executes.
)               If the top of the stack is not 0, jump to the column after 
                    the matching `(` after the current column executes.
?               Read an integer from STDIN.
!               Pop one value from the stack and print it to STDOUT as an
                    integer.
<space>         No-op

Ghi chú

  • v^hành động theo chu kỳ, do đó, vgiọng nói ở phía dưới sẽ sao chép thành phần ngăn xếp của giọng nói ^trên và giọng nói trên cùng sẽ sao chép từ giọng nói phía dưới. Hệ quả: Cả hai v^nhân đôi đỉnh của ngăn xếp trong một chương trình thoại đơn.
  • A (và kết hợp của nó )có thể được đặt trên các dòng khác nhau. Tuy nhiên , a )sẽ luôn nhìn vào ngăn xếp của giọng nói nơi (được đặt tương ứng , chứ không phải là ngăn xếp nơi )chính nó được đặt.
  • Các giá trị được tạo bởi ^vhướng dẫn hoạt động trên các giá trị hiện tại trước khi hoàn thành bất kỳ hoạt động nào khác trong cùng một cột.
  • ?!hoạt động khác với đặc điểm kỹ thuật được tìm thấy trên esolangs.org, vì vậy hãy chắc chắn kiểm tra với trình thông dịch sửa đổi một chút được cung cấp trong bài đăng này.

Đầu vào được đảm bảo có:

  • Khớp ngoặc đơn
  • Không quá một dấu ngoặc đơn trong một cột
  • Cùng số lượng ký tự trên mỗi dòng
  • Ít nhất một dòng
  • Không có cột có nhiều hơn một lệnh I / O ( !hoặc ?)
  • Một ký tự cho ăn theo hướng dẫn cho mỗi giọng nói
  • Không có nhân vật nào khác ngoài những nhân vật được đề cập ở trên

Ngôn ngữ đầu ra: Befunge-93

Befunge là ngôn ngữ dựa trên ngăn xếp có bộ đếm chương trình (PC; con trỏ đến lệnh hiện tại) di chuyển tự do trên lưới hai chiều. Nó bắt đầu ở góc trên cùng bên trái, di chuyển sang bên phải. Sân chơi có hình xuyến, tức là chuyển động của PC bao quanh cả hai cạnh. Befunge cũng có một ngăn xếp được khởi tạo với số lượng vô hạn. Befunge có các hoạt động sau:

Bạn có thể giả sử các đặc điểm sau của trình biên dịch / trình thông dịch Befunge-93:

  • Số nguyên là không giới hạn chính xác.
  • Nó cho phép lưới có kích thước bất kỳ.
  • Các tọa độ lưới (cho gp) dựa trên 0.

Chấm điểm

Để ngăn các bài nộp đơn giản tạo ra trình thông dịch Prelude trong Befunge và mã hóa nguồn Prelude vào đó, mục tiêu sẽ là giảm thiểu kích thước của mã nguồn Befunge kết quả.

Dưới đây được cung cấp một số chương trình Prelude. Phiên dịch của bạn sẽ được chạy trên tất cả những điều này. Điểm của bạn là tổng kích thước của các chương trình Befunge, miễn là tất cả chúng đều hợp lệ.

Trình dịch của bạn không nên được tối ưu hóa cụ thể đối với các trường hợp thử nghiệm này (ví dụ: bằng các chương trình Befunge viết tay cứng cho họ). Nếu tôi nghi ngờ bất kỳ câu trả lời nào khi làm như vậy, tôi có quyền thay đổi đầu vào hoặc tạo thêm đầu vào.

Đầu vào mẫu

In n-1xuống 0:

?(1-^!)

Logic VÀ:

?  (0)  
 ?(0  ) 
    1  !

Hợp lý HOẶC:

 ?   (0) 
? (0)    
   1  1 !

Kiểm tra tính chẵn lẻ của đầu vào (tức là modulo 2) của số không âm:

?(1-)   
 ^  v   
 v1-^^-!

Bình phương đầu vào:

 ^    
  ^+ !
?(1-) 

In số Fibonacci thứ n , trong đó n = 0tương ứng với 0 và n = 1tương ứng với 1:

0 v+v!
1   ^ 
?(1-) 

Dấu hiệu:

  1) v #  - !
 vv (##^v^+) 
?(#   ^   ## 

Bộ phận cho đầu vào không âm:

1 (#  1) v #  - 1+)   
     vv (##^v^+)      
?  v-(0 # ^   #       
 ?                    
   1+              1-!

Tất nhiên, chương trình của bạn phải thể hiện cùng một hành vi cho tất cả các trường hợp, ngay cả khi hành vi của chương trình mẫu đối với các số âm không được chỉ định.

Cuối cùng, trình dịch của bạn không nên dài một cách vô lý:

  • Nó phải được chứa trong bài Stack Stack
  • Nó sẽ xử lý các đầu vào mẫu dưới 10 phút trên một máy tính để bàn thông thường.

Lưu ý rằng đầu vào số cho Prelude hoặc Befunge được cung cấp dưới dạng dấu trừ tùy chọn theo sau là một hoặc nhiều chữ số thập phân, theo sau là một dòng mới. Đầu vào khác là hành vi không xác định.

Bạn có thể viết dịch giả của bạn bằng bất kỳ ngôn ngữ. Mã Befunge dịch ngắn nhất sẽ thắng.

Bảng xếp hạng

  • Sp3000 : 16430 byte

Tôi không hiểu: "Đẩy lên ngăn xếp giá trị hàng đầu trên ngăn xếp của giọng nói trên." Không phải là: "Đẩy lên ngăn xếp giá trị trên cùng của ngăn xếp của giọng nói trên."
Def

Nó nói prelude thực thi các giọng nói đồng thời, điều đó có nghĩa là chúng thực sự được thực thi trên một luồng riêng biệt hoặc tôi có thể thực hiện các lệnh đầu tiên trên tất cả các giọng nói (từ trên xuống dưới) sau đó là các lệnh thứ hai, v.v.
Def

@Deformyer Tôi đã thay đổi nó từ "trên" thành "của", nhưng tôi nghĩ "giá trị hàng đầu trên ngăn xếp" cũng không sai. Đối với tính đồng thời, không có bạn không cần phải thực sự giải thích chúng song song. Điều quan trọng là tất cả chúng đều hoạt động ở trạng thái trước của ngăn xếp và không có hoạt động nào trong một cột nhất định có thể ảnh hưởng đến bất kỳ hoạt động nào khác trong cột đó.
Martin Ender

Đừng để một số trường hợp kiểm tra vi phạm "Không có cột nào có nhiều hơn một lệnh I / O (! Hoặc?)?"
Fuwjax

@proudhaskeller Bên 1trong một vòng lặp, vì vậy nó có thể không được đẩy. Số 0 có thể đến từ số lượng 0 vô hạn bắt đầu trên ngăn xếp.
frageum

Câu trả lời:


10

Python 3, sẽ ghi điểm sau

from collections import defaultdict
from functools import lru_cache
import sys

NUMERIC_OUTPUT = True

@lru_cache(maxsize=1024)
def to_befunge_num(n):
    # Convert number to Befunge number, using base 9 encoding (non-optimal,
    # but something simple is good for now)

    assert isinstance(n, int) and n >= 0

    if n == 0:
        return "0"

    digits = []

    while n:
        digits.append(n%9)
        n //= 9

    output = [str(digits.pop())]

    while digits:
        output.append("9*")
        d = digits.pop()

        if d:
            output.append(str(d))
            output.append("+")

    output = "".join(output)

    if output.startswith("19*"):
        return "9" + output[3:]

    return output

def translate(program_str):
    if program_str.count("(") != program_str.count(")"):
        exit("Error: number of opening and closing parentheses do not match")

    program = program_str.splitlines()
    row_len = max(len(row) for row in program)
    program = [row.ljust(row_len) for row in program]
    num_stacks = len(program)


    loop_offset = 3
    stack_len_offset = program_str.count("(")*2 + loop_offset
    stack_offset = stack_len_offset + 1
    output = [[1, ["v"]], [1, [">"]]] # (len, [strings]) for each row
    max_len = 1 # Maximum row length so far

    HEADER_ROW = 0
    MAIN_ROW = 1
    FOOTER_ROW = 2
    # Then stack lengths, then loop rows, then stacks

    # Match closing parens with opening parens
    loop_map = {} # {column: (loop num, stack number to check, is_start)}
    loop_stack = []
    loop_num = 0

    for col in range(row_len):
        col_str = "".join(program[stack][col] for stack in range(num_stacks))

        if col_str.count("(") + col_str.count(")") >= 2:
            exit("Error: more than one parenthesis in a column")

        if "(" in col_str:
            stack_num = col_str.index("(")

            loop_map[col] = (loop_num, stack_num, True)
            loop_stack.append((loop_num, stack_num, False))
            loop_num += 1

        elif ")" in col_str:
            if loop_stack:
                loop_map[col] = loop_stack.pop()

            else:
                exit("Error: mismatched parentheses")


    def pad_max(row):
        nonlocal max_len, output

        while len(output) - 1 < row:
            output.append([0, []])

        if output[row][0] < max_len:
            output[row][1].append(" "*(max_len - output[row][0]))
            output[row][0] = max_len


    def write(string, row):
        nonlocal max_len, output

        output[row][1].append(string)
        output[row][0] += len(string)

        max_len = max(output[row][0], max_len)


    def stack_len(stack, put=False):
        return (to_befunge_num(stack) + # x
                str(stack_len_offset) + # y
                "gp"[put])


    def get(stack, offset=0):
        assert offset in [0, 1] # 1 needed for 2-arity ops

        # Check stack length
        write(stack_len(stack) + "1-"*(offset == 1) + ":0`", MAIN_ROW)

        pad_max(HEADER_ROW)
        pad_max(MAIN_ROW)
        pad_max(FOOTER_ROW)

        write(">" + to_befunge_num(stack + stack_offset) + "g", HEADER_ROW)
        write("|", MAIN_ROW)
        write(">$0", FOOTER_ROW)

        pad_max(HEADER_ROW)
        pad_max(MAIN_ROW)
        pad_max(FOOTER_ROW)

        write("v", HEADER_ROW)
        write(">", MAIN_ROW)
        write("^", FOOTER_ROW)


    def put(stack, value=""):
        put_inst = (value +
                    stack_len(stack) +
                    to_befunge_num(stack + stack_offset) +
                    "p")

        post_insts.append(put_inst)


    def pop(stack):
        put(stack, "0")


    def inc_stack_len(stack):
        post_insts.append(stack_len(stack) + "1+")
        post_insts.append(stack_len(stack, put=True))


    def dec_stack_len(stack):
        post_insts.append(stack_len(stack) + ":0`-") # Ensure nonnegativity
        post_insts.append(stack_len(stack, put=True))


    # Technically not necessary to initialise stack lengths per spec, but it makes it
    # more portable and easier to test against other Befunge interpreters

    for stack in range(num_stacks):
        write("0" + stack_len(stack, put=True), MAIN_ROW)

    for col in range(row_len):
        post_insts_all = []

        loop_start = False
        loop_end = False

        if col in loop_map:
            if loop_map[col][2]:
                loop_start = True
            else:
                loop_end = True

        if loop_start:
            loop_row = loop_offset + 2*loop_map[col][0]
            get(loop_map[col][1])

        elif loop_end:
            get(loop_map[col][1])
            write("!", MAIN_ROW)


        for stack in range(num_stacks-1, -1, -1):
            char = program[stack][col]
            post_insts = [] # Executed after the gets in reverse order, i.e. last added first

            if char in " ()":
                continue

            # Pre-inc, post-dec
            elif char.isdigit():
                inc_stack_len(stack)
                put(stack, char)

            elif char == "?":
                inc_stack_len(stack)
                put(stack, "&")

            elif char == "!":
                get(stack)
                post_insts.append(".91+," if NUMERIC_OUTPUT else ",")
                pop(stack)
                dec_stack_len(stack)

            elif char == "#":
                pop(stack)
                dec_stack_len(stack)

            elif char in "+-":
                get(stack, 1)
                get(stack)
                post_insts.append(char)
                pop(stack) # This one first in case of ! or 1!
                post_insts.append(stack_len(stack) + ":1`-:1\\`+") # Ensure >= 1
                post_insts.append(stack_len(stack, put=True))
                put(stack)                

            elif char in "^v":
                offset = -1 if char == "^" else 1

                get((stack + offset) % num_stacks)
                inc_stack_len(stack)
                put(stack)

            else:
                exit("Error: invalid character " + char)

            post_insts_all.append(post_insts)


        while post_insts_all:
            write("".join(post_insts_all.pop()), MAIN_ROW)

        if loop_start or loop_end:
            loop_row = loop_offset + 2*loop_map[col][0]

            pad_max(HEADER_ROW)
            pad_max(MAIN_ROW)
            pad_max(loop_row)
            pad_max(loop_row + 1)

            write(">v", HEADER_ROW)
            write("|>", MAIN_ROW)

            if loop_start:
                write(" ^", loop_row)
                write(">", loop_row + 1)

            else:
                write("<", loop_row)
                write(" ^", loop_row + 1)


    write("@", MAIN_ROW)
    return "\n".join("".join(row) for row_len, row in output)

if __name__ == '__main__':
    if len(sys.argv) < 3:
        exit("Usage: py -3 prefunge.py <input filename> <output filename>")

    with open(sys.argv[1]) as infile:
        with open(sys.argv[2], "w") as outfile:
            outfile.write(translate(infile.read()))

Chạy như thế py -3 prefunge.py <input filename> <output filename>.

Đó là một tuần chậm chạp đối với tôi, vì vậy cuối cùng tôi đã đủ chán để giải quyết câu hỏi sáu tháng tuổi này. Tôi hỏi tại sao không ai khác thử, nhưng tôi vẫn cảm thấy đau khi gỡ lỗi (và có lẽ vẫn còn lỗi cho tất cả những gì tôi biết).

Câu hỏi không cung cấp trình thông dịch Befunge-93, vì vậy tôi đã sử dụng trình thông dịch này , hơi khác so với thông số kỹ thuật. Hai điểm khác biệt chính là:

  • Nếu một char không tồn tại trong một hàng nhất định của chương trình, thì bạn không thể viết vào hàng đó. Điều này có nghĩa là bạn sẽ cần nhấn Enter nhiều lần để giới thiệu đủ dòng mới ở cuối . Nếu bạn thấy NaNs trong đầu ra, đây là nguyên nhân rất có thể.

  • Các ô lưới không được xác định trước về 0 - để thuận tiện, tôi đã bao gồm một số tiền xử lý trước trong các đầu ra Befunge, nhưng vì không cần thiết nên tôi có thể lấy đi khi bắt đầu ghi điểm.

Bố cục cốt lõi của các chương trình đầu ra là:

v [header row]
> [main row]
  [footer row]
  ---
   |
   | rows for loops (2 per loop)
   |
  ---
  [stack length row]
  ---
   |
   | rows for stack space (1 per voice)
   |
  ---

Không gian ngăn xếp nằm ngoài chương trình, do đó, bình luận Enter-spam mới từ trước đó.

Ý tưởng cốt lõi là gán cho mỗi giọng nói một hàng đóng vai trò là ngăn xếp của nó. Để duy trì các ngăn xếp này, chúng tôi cũng có một hàng có chiều dài ngăn xếp đặc biệt trong đó chiều dài của mỗi ngăn xếp được ghi lại trong một ô dọc theo hàng. Chương trình sau đó có rất nhiều gets và puts, ví dụ để in quy trình là:

  • Lấy tế bào tại y = stack_row[stack], x = stack_length[stack]
  • Thực hiện .91+,, tức là in dưới dạng số nguyên sau đó in một dòng mới
  • Thay thế ô ở các hợp đồng trên bằng 0 (để mô phỏng popping)
  • Giảm stack_length[stack]

Để thực hiện đánh giá đồng thời một cột, tất cả các ô cần thiết đều được đọc và các giá trị của chúng được giữ trên ngăn xếp trước khi bất kỳ ô nào được ghi vào (ví dụ: đối với ví dụ in, có thể có nhiều hướng dẫn hơn giữa các bước đầu tiên và thứ hai).

`, lớn hơn, được sử dụng để đảm bảo độ dài của ngăn xếp không bao giờ bị âm và để đẩy 0 khi ngăn xếp trống. Đây là nơi phân nhánh rõ ràng đến từ, nhưng tôi có một ý tưởng sẽ loại bỏ phân nhánh, điều này sẽ loại bỏ rất nhiều khoảng trắng từ hàng đầu tiên và thứ ba.

Đối với các vòng lặp, vì các vòng lặp Prelude có thể nhảy cả hai chiều, chúng tôi sử dụng hai hàng trên mỗi vòng lặp trong một cấu hình như thế này:

       >v                     >v
(cond) |>  (program)  (cond) !|>

        ^                     <
       >                       ^

Các vòng lặp này hiện chiếm phần lớn các byte, nhưng có thể dễ dàng đánh gôn bằng cách đặt chúng vào bộ mã p, mà tôi dự định sẽ làm sau khi tôi vui vì trình dịch đang hoạt động chính xác.

Dưới đây là một số ví dụ đầu ra cho ?(1-^!), tức là in n-1xuống 0:

v                        >6gv>v                      >6gv      >6gv                                 >6gv                   >6gv                           >6gv >v
>005p05g1+05p&05g6p05g:0`|  >|>05g1+05p105g6p05g1-:0`|  >05g:0`|  >-005g6p05g:1`-:1\`+05p05g6p05g:0`|  >05g1+05p05g6p05g:0`|  >.91+,005g6p05g:0`-05p05g:0`|  >!|>@
                         >$0^                        >$0^      >$0^                                 >$0^                   >$0^                           >$0^
                              ^                                                                                                                                <
                             >                                                                                                                                  ^

Square-the-input:

v                                >8gv      >8gv             >v      >6gv                                   >8gv      >8gv        >7gv      >7gv                                                            >8gv >v      >7gv
>005p015p025p25g1+25p&25g8p25g:0`|  >25g:0`|  >05g1+05p05g6p|>05g:0`|  >15g1+15p15g7p25g1+25p125g8p25g1-:0`|  >25g:0`|  >15g1-:0`|  >15g:0`|  >+015g7p15g:1`-:1\`+15p15g7p-025g8p25g:1`-:1\`+25p25g8p25g:0`|  >!|>15g:0`|  >.91+,015g7p15g:0`-15p@
                                 >$0^      >$0^                     >$0^                                   >$0^      >$0^        >$0^      >$0^                                                            >$0^         >$0^
                                                             ^                                                                                                                                                  <
                                                            >                                                                                                                                                    ^

Bộ phận (khuyến nghị đầu vào nhỏ):

v                                                                          >91+gv>v      >94+gv                                                         >95+gv      >95+gv        >93+gv      >93+gv                                                                    >93+gv      >93+gv               >v      >93+gv                                                     >93+gv >v      >92+gv                  >v      >92+gv                                       >92+gv                                       >91+gv                                       >93+gv                     >91+gv                       >92+gv      >92+gv        >91+gv      >91+gv                                                                                      >92+gv >v                        >91+gv      >91+gv                                     >91+gv >v                        >95+gv      >95+gv                                     >95+gv
>009p019p029p039p049p09g1+09p109g91+p29g1+29p&29g93+p39g1+39p&39g94+p09g:0`|    >|>39g:0`|    >009g91+p09g:0`-09p29g1+29p29g93+p49g1+49p149g95+p49g1-:0`|    >49g:0`|    >29g1-:0`|    >29g:0`|    >-029g93+p29g:1`-:1\`+29p29g93+p+049g95+p49g:1`-:1\`+49p49g95+p29g:0`|    >29g:0`|    >19g1+19p19g92+p|>29g:0`|    >09g1+09p109g91+p19g1+19p19g92+p29g1+29p029g93+p29g:0`|    >!|>19g:0`|    >029g93+p29g:0`-29p|>19g:0`|    >09g1+09p09g91+p019g92+p19g:0`-19p19g:0`|    >019g92+p19g:0`-19p29g1+29p29g93+p09g:0`|    >009g91+p09g:0`-09p19g1+19p19g92+p29g:0`|    >19g1+19p19g92+p09g:0`|    >19g1+19p19g92+p19g1-:0`|    >19g:0`|    >09g1-:0`|    >09g:0`|    >-009g91+p09g:1`-:1\`+09p09g91+p+019g92+p19g:1`-:1\`+19p19g92+p029g93+p29g:0`-29p19g:0`|    >!|>09g1+09p109g91+p09g1-:0`|    >09g:0`|    >+009g91+p09g:1`-:1\`+09p09g91+p09g:0`|    >!|>49g1+49p149g95+p49g1-:0`|    >49g:0`|    >-049g95+p49g:1`-:1\`+49p49g95+p49g:0`|    >.91+,049g95+p49g:0`-49p@
                                                                           >$0  ^        >$0  ^                                                         >$0  ^      >$0  ^        >$0  ^      >$0  ^                                                                    >$0  ^      >$0  ^                       >$0  ^                                                     >$0  ^         >$0  ^                          >$0  ^                                       >$0  ^                                       >$0  ^                                       >$0  ^                     >$0  ^                       >$0  ^      >$0  ^        >$0  ^      >$0  ^                                                                                      >$0  ^                           >$0  ^      >$0  ^                                     >$0  ^                           >$0  ^      >$0  ^                                     >$0  ^
                                                                                  ^                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        <
                                                                                 >                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          ^
                                                                                                                                                                                                                                                                                                          ^                                                                        <
                                                                                                                                                                                                                                                                                                         >                                                                          ^
                                                                                                                                                                                                                                                                                                                                                                                                                    ^                                                                                                                                                                                                                                                                                                                                              <
                                                                                                                                                                                                                                                                                                                                                                                                                   >                                                                                                                                                                                                                                                                                                                                                ^

Ngoài ra còn có một loạt các tối ưu hóa nhỏ khác xuất hiện trong đầu, như thay thế 07p07gbằng :07p, nhưng tôi đang thực hiện từng bước này :)


Vì thế. Nhiều lắm. Miễn phí. Thời gian.
Trình tối ưu hóa

1
Will score later2 năm và đếm! :)
HyperNeutrino
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.