Prindeal (phát âm là prin-dee-al ) là một ngôn ngữ lập trình bí truyền mới chỉ có bốn lệnh: pr int , in crement , de crement và al ias . Bất chấp sự tối giản của nó, các phép toán phức tạp có thể được thực hiện trong Prindeal bằng cách kết hợp khéo léo bốn lệnh.
Nhiệm vụ của bạn trong thử thách chơi gôn mã này là viết chương trình ngắn nhất có thể chạy mã Prindeal.
Thông số kỹ thuật dài nhưng tôi đã cố gắng làm cho nó rõ ràng nhất có thể và tôi tin rằng nếu bạn nỗ lực để học Prindeal, bạn sẽ thấy nó khá thanh lịch!
Giải thưởng nội bộ
Sơ chế
Trước khi chương trình Giải thưởng có thể được diễn giải, những điều này cần phải được loại bỏ khỏi chương trình theo thứ tự sau:
- Bất cứ điều gì sau khi một
#
dấu hiệu đến cuối dòng, nó cộng với#
chính nó. (Đây là những bình luận.) - Trailing khoảng trắng trên bất kỳ dòng.
- Dòng hoàn toàn trống rỗng.
Ví dụ, chương trình Giải thưởng
p cat #The next line has 7 trailing spaces.
p dog
#p mouse
sẽ được xử lý trước thành
p cat
p dog
Từ đây trở đi, chúng tôi sẽ giả định bước tiền xử lý này đã được thực hiện.
Biến
Chúng ta cần nhanh chóng xác định các biến trước khi hiển thị cách chúng được sử dụng.
Các biến (và tham chiếu đến các biến) là những gì được truyền vào các đối số của các lệnh Prindeal. Các biến luôn luôn là toàn cục , do đó, sửa đổi cho một biến, bất kể chúng xảy ra ở đâu, được phản ánh ở mọi nơi.
Mỗi biến chứa một số nguyên không chính xác tùy ý không âm (0, 1, 2, 3, ...). Các biến không cần phải được khởi tạo trước - chúng luôn bắt đầu bằng giá trị 0 trong lần đầu tiên chúng được sử dụng hoặc được gọi cho.
Một tên biến có thể là bất kỳ chuỗi chữ và số không trống nào không bắt đầu bằng một chữ số - [a-zA-Z_][0-9a-zA-Z_]*
trong regex . Chúng là trường hợp nhạy cảm, vì vậy spiny_lumpsuck3r
và Spiny_lumpsuck3r
là các biến khác nhau.
Chấp hành
Prindeal là một ngôn ngữ lập trình bắt buộc . Khi một chương trình Prindeal được chạy, các câu lệnh của nó được thực thi từ trên xuống dưới theo thứ tự và sau đó chương trình kết thúc.
Mỗi dòng không thụt lề trong chương trình Prindeal là một câu lệnh liên quan đến việc thực thi một lệnh duy nhất có thể hoặc không thể lấy các đối số.
Các dòng thụt lề chỉ xảy ra sau các lệnh bí danh . Cụ thể, chính xác ba dòng thụt vào với các khoảng trắng đơn xảy ra sau mỗi lệnh bí danh và được coi là một phần của nó. Vì vậy, các tuyên bố bí danh thực sự dài bốn dòng. (Chúng có thể là một dòng, bốn chỉ đơn giản là dễ đọc hơn.)
Tuyên bố không bí danh
Ngoại trừ bí danh , mọi tuyên bố trong chương trình Giải thưởng đều có dạng:
[command name] [argument 1] [argument 2] [argument 3] ...
Có thể có một số lượng đối số tùy ý (bao gồm cả không có đối số). Mỗi đối số luôn là một biến hoặc (như chúng ta sẽ thấy khi thảo luận về bí danh ) một tham chiếu đến một biến .
Sau khi thực hiện xong, mỗi câu lệnh được gắn cờ là thất bại hoặc thành công tùy thuộc vào việc có gặp phải lỗi hay không. (Điều này chỉ thực sự quan trọng khi chúng ta sử dụng bí danh .)
Bản in , tăng và giảm tích hợp là các câu lệnh có dạng ở trên. Đây là những gì họ làm:
print có tên lệnh
p
và lấy một đối số. Nó in tên của biến được truyền vào và giá trị của nó (theo số thập phân) được phân tách bằng "=", sau đó là một dòng mới. Nó luôn được gắn cờ là một thành công .Ví dụ, chương trình Giải thưởng
p _MyVariable_321 p screaming_hairy_armadillo
sẽ xuất
_MyVariable_321 = 0 screaming_hairy_armadillo = 0
bởi vì tất cả các biến bắt đầu từ 0. (Không gian trước và sau dấu bằng được yêu cầu.)
gia tăng có tên lệnh
i
và nhận một đối số. Nó tăng giá trị của biến được truyền vào 1. Nó luôn được gắn cờ là thành công ..Ví dụ, chương trình
i alpaca p alpaca i alpaca p alpaca
sẽ xuất
alpaca = 1 alpaca = 2
Lưu ý cách
alpaca
tăng từ 0 lên 1 mặc dù trước đó chưa bao giờ được truy cập.decrement có tên lệnh
d
và lấy một đối số. Nếu biến được truyền vào là khác 0 thì giá trị của nó bị giảm đi 1 và câu lệnh được gắn cờ là thành công . Nếu biến được truyền vào là 0 thì không có gì được thực hiện và câu lệnh được gắn cờ là thất bại .Ví dụ, chương trình
i malamute p malamute d malamute #success p malamute d malamute #failure p malamute d akita #failure p akita
sẽ xuất
malamute = 1 malamute = 0 malamute = 0 akita = 0
Lưu ý rằng việc giảm một biến có giá trị 0 là cách duy nhất để tạo ra lỗi .
Các bí danh Tuyên bố và bí danh Commands
Lệnh bí danh có một cú pháp đặc biệt và mạnh nhất vì nó có thể được sử dụng để xác định các lệnh mới. Tên lệnh bí danh là a
và một câu lệnh bí danh có dạng:
a [name of new command]
[statement A]
[statement B]
[statement C]
Trong đó mỗi [statement X]
đại diện cho bất kỳ câu lệnh không bí danh nào , tức là một cái gì đó với biểu mẫu [command name] [argument 1] [argument 2] [argument 3] ...
.
Tên của lệnh bí danh [name of new command]
có thể là bất kỳ chuỗi chữ và số không trống nào không bắt đầu bằng một chữ số - [a-zA-Z_][0-9a-zA-Z_]*
trong biểu thức chính quy.
(Đây là cùng một tập hợp các tên như các biến nhưng các lệnh và biến bí danh là những thứ khác nhau được sử dụng ở những nơi khác nhau . Một biến có thể được đặt tên giống như một lệnh không có hậu quả xấu.)
Khi một câu lệnh bí danh được thực thi, một lệnh mới sẽ được thêm vào cùng với bốn p
i
d
a
lệnh gốc . Lệnh mới có thể được sử dụng như các [command name]
câu lệnh trong và được gọi với các đối số giống như bất kỳ lệnh không bí danh nào khác .
Khi một câu lệnh có tên lệnh bí danh được thực thi, chính xác hai câu lệnh nữa từ câu lệnh bí danh ban đầu của nó được chạy:
[statement A]
luôn luôn chạy[statement B]
đang chạy nếu[statement A]
là một thành công[statement C]
đang chạy nếu[statement A]
là một thất bại
Các câu A, B và C luôn được chạy một cách lười biếng , tức là chúng được đánh giá một cách nhanh chóng tại thời điểm chúng được chạy.
Khi thực hiện xong, lệnh bí danh được gắn cờ với cùng một thành công hoặc thất bại giống như câu lệnh B hoặc C, bất kỳ lệnh nào được thực thi . ( bản thân các tuyên bố bí danh không cần phải được gắn cờ vì chúng không thể xảy ra trong chính chúng.)
Bí danh Ví dụ 1
Nói rằng chúng tôi muốn một lệnh mới làm tăng biến
frog
hai lần. Tuyên bố bí danh này đạt được nó:a increment_frog_twice i frog i frog d frog
Câu lệnh A (
i frog
) luôn được chạy và luôn được gắn cờ là thành công, vì vậy câu lệnh B (i frog
) cũng luôn luôn chạy vàfrog
do đó biến được tăng thêm 2.increment_frog_twice
Lệnh luôn được gắn cờ là thành công vì câu lệnh B luôn chạy và B luôn luôn là một thành công . Tuyên bố C (d frog
) không bao giờ được chạy.Vì vậy, đầu ra để
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frog
sẽ là
frog = 0 frog = 2
Chúng ta có thể khái quát ví dụ này để bất kỳ biến nào có thể được tăng lên hai lần bằng cách đưa ra lệnh đối số.
Trong một câu lệnh bí danh, các số nguyên dương 1, 2, 3, v.v. đại diện cho các đối số 1, 2, 3, v.v. được truyền vào lệnh bí danh. (Các đối số này có thể là các biến đơn giản hoặc tham chiếu đến chính các biến.) Các số này chỉ có thể xuất hiện trong các đối số của câu lệnh A, B và C trong câu lệnh bí danh . Nó không có ý nghĩa đối với họ xuất hiện ở nơi khác.
Bí danh Ví dụ 2
Điều này khái quát hóa ví dụ cuối cùng - bất kỳ biến nào được truyền vào
increment_twice
sẽ được tăng thêm 2 vì1
là tham chiếu đến đối số đầu tiên được truyền vào:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toad
Đầu ra của chương trình này sẽ là
toad = 0 toad = 2
Sau đó chúng ta có thể đặt bí danh cho một lệnh khác có hai đối số và gọi
increment_twice
cả hai:a increment_twice i 1 i 1 d 1 #never reached a increment_both_twice increment_twice 1 increment_twice 2 d 1 #never reached increment_both_twice platypus duck p platypus p duck
Đầu ra ở đây sẽ là
platypus = 2 duck = 2
Điều quan trọng là phải nhận ra rằng các lệnh bí danh có thể được đệ quy, vì đây là nơi sức mạnh thực sự của chúng nằm. Ví dụ: chúng ta có thể tạo một lệnh đặt bất kỳ biến nào được truyền vào 0:
Bí danh Ví dụ 3
Các
set_to_zero
lệnh có một đối số và thiết lập biến của nó để 0 và được gắn cờ là một thành công khi thực hiện:a set_to_zero d 1 set_to_zero 1 i _dummy_ i oryx i oryx i oryx p oryx set_to_zero oryx p oryx
Đầu ra của chương trình này sẽ là
oryx = 3 oryx = 0
Điều đang xảy ra là khi
set_to_zero oryx
được chạy,d 1
giảm thành côngoryx
từ 3 xuống 2, sau đóset_to_zero 1
được gọi, tương tự như gọiset_to_zero oryx
lại. Vì vậy, lặp đi lặp lại quá trình cho đến khid 1
là một thất bại , ngăn chặn sự đệ quy và tăng các_dummy_
biến do đó, một thành công được sản xuất.
Thử thách
Viết chương trình có thể thực thi mã Prindeal chính xác như được mô tả ở trên. Lấy mã Prindeal thông qua stdin, dòng lệnh hoặc dưới dạng tệp văn bản. In kết quả đầu ra của chương trình Prindeal lên thiết bị xuất chuẩn hoặc ngôn ngữ thay thế gần nhất với ngôn ngữ của bạn.
Ngoài ra, bạn có thể viết một hàm lấy mã dưới dạng chuỗi và in hoặc trả về chuỗi đầu ra.
Ngoài ra, bạn có thể giả định rằng:
- Mã Prindeal đầu vào sẽ chỉ chứa các dòng mới và ASCII có thể in được và (tùy chọn) mà nó kết thúc bằng một dòng trống.
- Mã đầu vào sẽ hợp lệ Prindeal - được hình thành tốt và đúng về mặt cú pháp.
- Chạy mã sẽ không tạo ra bất kỳ vòng lặp vô hạn cũng như bất kỳ tham chiếu không hợp lệ nào cho các lệnh chưa được xác định hoặc các đối số chưa được đưa ra.
- Các tên lệnh
p
,i
,d
, vàa
sẽ không bao giờ được aliased kết thúc. (Bạn có thể không cho rằng các biến sẽ không có các tên này.)
Ngoài ra, sẽ không có vấn đề gì nếu các giá trị biến của bạn không thực sự là các số nguyên chính xác tùy ý vì chỉ các số nhỏ hơn khoảng 1000 sẽ được kiểm tra. Cũng không sao nếu ngôn ngữ của bạn có giới hạn đệ quy (như Python ) mà các chương trình Prindeal phức tạp hơn có thể gặp phải miễn là chương trình thử nghiệm bên dưới hoạt động.
Chương trình kiểm tra
Đây là một chương trình Prindeal lớn, xây dựng các hoạt động cộng, nhân và lũy thừa thông qua việc sử dụng các biến giả (bắt đầu _
bằng quy ước) và nhiều bí danh trợ giúp:
#Command Definitions:
a s #flag as a success
i _
d _
d _
a f #flag as a failure
d _
d _
d _
a z #1 = zero
d 1
z 1
s
a n #1 = one
z 1
i 1
s
a move #2 += 1, 1 = zero
moveH 1 2
move 1 2
s
a moveH #move helper
d 1
i 2
f
a dupe #2 += 1, 3 += 1, 1 = zero
dupeH1 1 2 3
dupe 1 2 3
s
a dupeH1 #dupe helper
d 1
dupeH2 2 3
f
a dupeH2 #dupe helper
i 1
i 2
s
a copy #2 = 1
z 2
copyH 1 2
s
a copyH #copy helper
dupe 1 2 _copy
move _copy 1
s
a addTo #1 += 2
copy 2 _add
#testing comments #
move _add 1#in weird places # just because #
s
#it's a g##d idea
###
a add #1 = 2 + 3
#its a good idea
z 1
addH 1 2 3
s
##
#
a addH #add helper
#this is a comment
addTo 1 2 #as is this
addTo 1 3
s
a mul #1 = 2 * 3
mulH1 1 2
mulH2 1 3
s
a mulH1 #mul helper
z 1
copy 2 _mul
s
a mulH2 #mul helper
mulH3 1 2
mulH2 1 2
s
a mulH3 #mul helper
d _mul
addTo 1 2
f
a mulBy #1 *= 2
mul _mulBy 1 2
copy _mulBy 1
s
a pow #1 = 2^3
powH1 1 3
powH2 1 2
s
a powH1 #pow helper
n 1
copy 2 _pow
s
a powH2 #pow helper
powH3 1 2
powH2 1 2
s
a powH3 #pow helper
d _pow
mulBy 1 2
f
#Running Tests:
p A
p B
p C
n A #A = 1
n B #B = 1
add C A B #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B #d = d * B = 6 * 3 = 18
p ____
p d
d A #A = A - 1 = 1 - 1 = 0
mulBy d A #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C
(Nếu bạn đang chơi xung quanh với mã này, hãy lưu ý rằng nhiều lệnh sẽ thất bại nếu cùng một biến được đưa ra nhiều lần làm đối số. Điều này có thể dễ dàng được sửa nhưng mã kết quả dài hơn.)
Trình thông dịch Prindeal của bạn sẽ có thể tạo đầu ra chính xác:
A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729
Chấm điểm
Mã ngắn nhất tính bằng byte thắng. Tiebreaker đi đến trình trước đó.
Phần thưởng Brownie: Viết một chương trình thú vị trong Prindeal. Tôi đã thực hiện phép cộng và phép nhân, bạn có thể thực hiện phép trừ hoặc phép chia không?
p
, và sau đóp p
, sẽ in 1, phải không?