Mẹo lưu trữ trong ngôn ngữ chơi gôn


16

Tôi đang viết một ngôn ngữ chơi golf.

Bạn có đề xuất các biến, ngăn xếp, băng, thanh ghi, vv để lưu trữ bằng ngôn ngữ golf-code không? Điều gì về đầu vào ngầm?

Định nghĩa thô:

  • Một biến chỉ đơn giản là một tên (thường là một ký tự dài trong các ngôn ngữ chơi gôn) mà một giá trị có thể được gán cho và sau đó được lấy bằng tên đó.
  • Một thanh ghi giống như một biến, nhưng nó có các lệnh riêng (thường là một byte) để thiết lập / nhận giá trị.
  • Một ngăn xếp là một mảng / danh sách các giá trị có độ dài thay đổi, trong đó các giá trị được thêm gần đây nhất (các giá trị "trên cùng") là các giá trị đang được sửa đổi.
  • Một hàng đợi giống như một ngăn xếp, ngoại trừ các giá trị "ở dưới cùng " là các giá trị đang được sửa đổi.
  • Một băng là một mảng / danh sách các giá trị tĩnh trong đó mỗi giá trị có một chỉ mục. Sự khác biệt chính giữa ngăn xếp và băng là các giá trị trên băng được sửa đổi tại chỗ.

Tôi sẽ đánh giá cao để biết những lợi thế và bất lợi của từng tùy chọn cho các kịch bản khác nhau và tổng thể. Xin vui lòng tránh ý kiến, và báo cáo dự phòng với lý luận.


1
Tôi nghĩ bạn nên bao gồm một định nghĩa về các điều khoản này trong câu hỏi của bạn
Kritixi Lithos

2
@Firthize Về mặt kỹ thuật, phương pháp lưu trữ không phụ thuộc vào loại ngôn ngữ chơi gôn mà bạn tạo ra, loại ngôn ngữ chơi golf phụ thuộc vào phương thức lưu trữ ...
ETHproductions

1
@ETHproductions Họ hoàn toàn phụ thuộc lẫn nhau.
Gây tử vong vào

1
Tôi đã thêm một số định nghĩa sơ bộ về các điều khoản lưu trữ khác nhau, vui lòng chỉnh sửa hoặc khôi phục nếu bạn không thích chúng.
Sản phẩm ETH

2
Có một mối quan hệ rất chặt chẽ giữa phương thức lưu trữ và loại ngôn ngữ, nhưng tôi nghĩ bạn phải xem xét cả hai. Ví dụ: các ngôn ngữ "bắt buộc" (những ngôn ngữ thực hiện các hướng dẫn của chúng hoàn toàn từ trái sang phải) có thể dựa trên ngăn xếp (CJam, 05AB1E), dựa trên băng (BrainF ***) hoặc hoàn toàn khác (V, sử dụng một chuỗi 2D lớn gọi là "bộ đệm", cùng với một vài thanh ghi). Ngoài ra còn có các ngôn ngữ dựa trên tiền tố (Pyth), ngôn ngữ dựa trên infix (Japt, Pip và về cơ bản mọi ngôn ngữ chính), ngôn ngữ dựa trên liên kết (Jelly), v.v ... tất cả đều hầu như không sử dụng bất kỳ phương pháp nào được đề cập.
Sản xuất ETH

Câu trả lời:


4

Tất cả các loại lưu trữ liên quan đến việc lưu trữ một cái gì đó tại một điểm và lấy nó sau. Để thực hiện việc này chỉ trong một thao tác, bạn nên thực hiện lưu trữ hoặc truy xuất tự động và chỉ định vị trí của giá trị được lưu trữ trong hoạt động khác.

Nghĩa là, đối với lưu trữ rõ ràng, bạn có thể tạo một toán tử để truy xuất giá trị được tính thứ n trước thao tác này hoặc đặt lại giá trị hiện tại sau n hoạt động. Ngoài ra, bạn có thể sử dụng vị trí tuyệt đối từ khi bắt đầu chương trình hoặc thực hiện nhiều việc khác như tự động xóa một số phần tử sau một số thao tác (như trong ngăn xếp). Bạn cũng có thể tạo nhiều toán tử, truy xuất từ ​​các bản sao khác nhau của bộ lưu trữ có hoặc không có các thao tác tự động này. Và bạn nên cố gắng làm cho số lượng tối đa cần thiết để chỉ định trong các hoạt động nhỏ một cách hợp lý, để bạn có thể chỉ định một toán tử cho mỗi số.

Nhưng trong hầu hết các trường hợp, bạn thậm chí không cần một nhà điều hành và ngôn ngữ sẽ làm điều này hoàn toàn. Đó là khi bạn cần xem xét một mô hình chuẩn hơn như ngăn xếp hoặc hàng đợi. Thành công nhất bây giờ dường như là lập trình ngầm, thậm chí không đề cập đến việc lưu trữ trực tiếp.

Nếu bạn muốn thiết kế một mô hình mới như vậy, bạn có thể thử mở rộng các đánh giá như một dag và thử nghĩ về một dag mặc định nếu không có gì khác được chỉ định. Nhiều khả năng, mặc định chỉ là một cái cây, ngoại trừ việc nhiều lá có thể được liên kết với cùng một đầu vào. Ví dụ, bạn có thể sử dụng một hàng đợi cho một cây cân bằng, hoặc một chồng cho một cây sâu, nơi lá hầu như không đổi, hoặc một cái gì đó như Jelly cho một cây sâu, nơi lá chủ yếu là bản sao của đầu vào.

Nhưng lưu ý rằng, bạn có thể mã hóa hình dạng của cây nhị phân chỉ trong 2 bit cho mỗi toán tử. Vì vậy, nếu ngôn ngữ của bạn có ít hơn 64 toán tử, bạn thực sự có thể bỏ qua các mô hình truyền thống và chỉ mã hóa cây hoàn chỉnh trong các bit dự phòng (gọi chúng là cờ kết hợp_parent và cờ_leaf). Ngay cả khi có nhiều toán tử hơn, bạn có thể tạo một mặc định khá tốt (chẳng hạn như mô hình của Jelly) và 3 bộ sửa đổi để thay đổi nó.

Bạn có thể sử dụng cùng một mô hình để lưu trữ ẩn và rõ ràng để thuận tiện, nhưng bạn không phải làm vậy. Ví dụ: bạn có thể sử dụng ngăn xếp để lưu trữ ẩn, nhưng không sử dụng các phần tử pop trong bộ lưu trữ rõ ràng (hoặc trong một bộ lưu trữ rõ ràng khác ngoài bộ lưu trữ ẩn). Có khả năng nó sẽ không được gọi là một ngăn xếp trong tài liệu cuối cùng, nhưng bạn hiểu ý.

Để tham khảo, kích thước của mã hóa hoàn hảo của cây nhị phân là logarit của các số Catalan . Và kích thước của mã hóa hoàn hảo của một "nhị phân" là logarit của A082161 , nhưng rõ ràng là không thực tế. Điều này giả sử một toán tử có thứ tự đối số khác nhau đặt hai toán tử khác nhau, thêm một bit khác khi không.

Đôi khi bạn vẫn có thể muốn các biến cho các vòng lặp. Có thể viết lại các vòng theo những cách khác. Nhưng nếu bạn thực sự cần nó, đừng sử dụng cấu trúc 1 byte ngoài tên để xác định biến. trừ khi bạn chỉ sử dụng các giá trị được khởi tạo trước, việc sử dụng cờ 1 bit sẽ hiệu quả hơn nếu bạn đang đọc hoặc viết biến này.


7

Tôi đề nghị tất cả trong số họ!

Nghiêm trọng hơn, tất cả chúng đều có ích một số thời gian, và càng nhiều càng tốt! Đầu vào ngầm không bao giờ là xấu , chỉ cần có một cờ để tắt nó. Các biến rất hữu ích vì vậy chúng không cần phải được tìm thấy trên một ngăn xếp hoặc băng; tương tự với các thanh ghi. Ngăn xếp là hữu ích để lưu trữ dữ liệu, và băng cũng vậy. Tôi khuyên bạn nên cố gắng thực hiện nhiều, giả sử một ngăn xếp và các thanh ghi, hoặc một ngăn xếp và các biến, như GolfScript. Nếu bạn có thể làm cho mỗi chức năng một byte, thì ngôn ngữ của bạn có thể sẽ hiệu quả trong việc chơi golf, vì bạn có thể sử dụng các lợi thế của từng chức năng.

Một ví dụ:

  • Nói rằng tôi muốn lấy hai số làm đầu vào và thêm độ dài chuỗi của chúng
  • Các biến có thể tốt hơn cho điều này (một ngăn xếp có thể không)
  • Mã ví dụ trong GolfScript (với đầu vào ẩn):

    ~,\,+
    ~ # Eval input (e.g. "1 2" > 1, 2)
    , # Take Length
    \ # Reverse items on the stack
    , # Take Length
    + # Add
      # Implicit output
    

    Tuy nhiên, với các biến (tôi biết nó dài hơn, nó không phải trao đổi địa điểm trên ngăn xếp):

    ~,:a;,:b;ab+
    ~ # Eval input
    , # Get length
    :a# Store in "a"
    ; # Discard value left on stack
    , # Length
    :b# Store in "b"
    ; # Discard
    a # Recall a
    b # Recall b
    + # Add
      # Implicit output
    

Quá tải

Một điều khác có thể hữu ích là quá tải. Ví dụ: nếu bạn có chức năng lưu trữ biến, có thể nó có thể được sử dụng như một đơn nguyên (chức năng một đầu vào; tôi không chắc chắn về thuật ngữ ngoài J / K / APL) để thêm vào ngăn xếp hoặc băng.

Thí dụ:

c12 # Stores "1" into register 2
c1] # Pushes "1" onto the stack ("]" is used to denote the end of the monad)

Có lẽ nếu một đối số được gọi với các loại sai, nó sẽ được thêm vào hàng đợi sau đó được sử dụng để điền vào các giá trị nếu ngăn xếp trống?
Esolanging Fruit

5

Tôi khuyên bạn nên có một số lưu trữ có thể sử dụng nhanh chóng (từ băng đã cho - băng, hàng đợi, ngăn xếp) và một số lưu trữ vĩnh viễn (biến, thanh ghi) để mọi thứ không bị cản trở trong khi chương trình đang làm gì đó không liên quan. Tôi muốn nói rằng nhiều hơn nữa sẽ hiếm khi cung cấp bất cứ điều gì và để lại nhiều ký tự miễn phí cho các hướng dẫn 1 byte hơn.

Từ các định nghĩa được đưa ra, những cái tôi nghĩ sẽ hoạt động tốt nhất sẽ là một ngăn xếp và các thanh ghi.
Một ngăn xếp, bởi vì một băng phải sử dụng các chức năng chỉ để lưu trữ một thứ mới trong đó, trong khi một ngăn xếp nên có các chức năng đẩy và bật đơn giản (thường được xây dựng trong các lệnh). Các thanh ghi, vì chúng mất ít byte hơn so với các biến và nếu bạn cần lưu trữ nhiều hơn 2-4 thứ khác nhau cho một thứ gì đó, thì bạn đang làm sai.

Đừng giới hạn các chức năng của chúng chỉ với những gì tên hoặc định nghĩa của chúng gợi ý máng - một số chức năng như put the 1st thing of the stack on top of the stackchắc chắn có thể giúp ích.


5

Về cơ bản có hai loại lưu trữ cần được xử lý khác nhau; truy cập vào các giá trị được tạo gần đây và / hoặc các đầu vào; và lưu trữ lâu dài.

Để lưu trữ lâu dài, các biến dường như hoạt động tốt nhất; bất cứ điều gì với số lượng tùy chọn giới hạn không có tỷ lệ (mặc dù các ngôn ngữ có thuộc tính này, như Jelly, dù sao cũng có thể làm khá tốt ngay cả trên các tác vụ cỡ trung bình). Tuy nhiên, hầu như không cần cung cấp tên biến khi lưu trữ biến, hầu hết thời gian; chỉ cần có một lệnh để lưu trữ một giá trị trong biến và tự động tạo các tên theo một mẫu có thể dự đoán được để việc lưu trữ và truy xuất giá trị có thể là một lệnh trong mỗi trường hợp đơn giản. (Ví dụ: bạn có thể có các lệnh để khôi phục biến được gán gần đây nhất, biến thứ hai gần đây nhất, thứ ba gần đây nhất, v.v., lên đến một số cố định nhỏ, cộng với một lệnh chung lấy một đối số.)

Đối với lưu trữ ngắn hạn, bạn muốn càng nhiều càng ẩn. Theo mặc định, hầu như tất cả các ngôn ngữ chơi gôn đều kết nối đầu ra của một lệnh với đầu vào của lệnh tiếp theo; cách chính xác trong đó điều này được thực hiện khác nhau từ ngôn ngữ này sang ngôn ngữ khác nhưng thường đi đến cùng một điều. Tuy nhiên, đầu vào thứ hai cho lệnh 2 đầu vào thú vị hơn. Lấy nó từ ngăn xếp hoạt động tốt trong trường hợp giá trị chỉ được sử dụng một lần, nhưng không mở rộng tốt khi sử dụng lại các giá trị. (Cố gắng tránh các nguyên hàm thao tác ngăn xếp; nếu bạn phải sử dụng chúng, bạn sẽ lãng phí rất nhiều byte.) Ngoài ra, sử dụng đầu vào của người dùng hoặc một biến được gán gần đây vì đối số thứ hai ẩn có xu hướng tiết kiệm một vài byte chương trình đơn giản, mặc dù bạn '

Trong một ngôn ngữ chơi gôn hiện tại tôi đang làm việc, tôi sử dụng kết hợp một cơ chế rất rẻ (một bit ) để chỉ định hình dạng của cây phân tích, tự động điền vào các đối số bị thiếu từ đầu vào của người dùng và một điểm kiểm tra cách tiếp cận làm cho nó có thể đặt mặc định cho các đối số bị thiếu sang một thứ khác (cộng với nhiều trường hợp đặc biệt). Tại một số điểm tôi có thể sẽ thêm các biến để truyền đạt thông tin khoảng cách lớn hơn dọc theo chương trình.


0

Tôi đề nghị một băng và một đăng ký.

Tôi thích băng hơn ngăn xếp vì băng có xu hướng có ít yếu tố hơn, làm cho việc thao tác với chúng trở nên dễ dàng. Ngoài ra, việc có thể đặt các phần tử trong băng vào thanh ghi và ngược lại làm cho mã ngắn, dễ dàng.

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.