1. Khái niệm cơ bản
Để hiểu Brainfuck, bạn phải tưởng tượng một mảng vô hạn các ô được khởi tạo bởi 0
mỗi ô.
...[0][0][0][0][0]...
Khi chương trình khốn khổ bắt đầu, nó sẽ trỏ đến bất kỳ ô nào.
...[0][0][*0*][0][0]...
Nếu bạn di chuyển con trỏ sang phải, >
bạn đang di chuyển con trỏ từ ô X sang ô X + 1
...[0][0][0][*0*][0]...
Nếu bạn tăng giá trị ô, +
bạn nhận được:
...[0][0][0][*1*][0]...
Nếu bạn tăng giá trị ô một lần nữa, +
bạn nhận được:
...[0][0][0][*2*][0]...
Nếu bạn giảm giá trị ô, -
bạn nhận được:
...[0][0][0][*1*][0]...
Nếu bạn di chuyển con trỏ sang trái, <
bạn đang di chuyển con trỏ từ ô X sang ô X-1
...[0][0][*0*][1][0]...
2. Đầu vào
Để đọc ký tự, bạn sử dụng dấu phẩy ,
. Những gì nó làm là: Đọc ký tự từ đầu vào chuẩn và ghi mã ASCII thập phân của nó vào ô thực tế.
Hãy xem bảng ASCII . Ví dụ, mã thập phân của !
là 33
, trong khi a
là 97
.
Vâng, hãy tưởng tượng bộ nhớ chương trình BF của bạn trông như thế nào:
...[0][0][*0*][0][0]...
Giả sử đầu vào tiêu chuẩn là viết tắt của a
, nếu bạn sử dụng ,
toán tử dấu phẩy , những gì BF làm là đọc a
mã ASCII thập phân 97
vào bộ nhớ:
...[0][0][*97*][0][0]...
Bạn thường muốn nghĩ theo cách đó, tuy nhiên sự thật thì phức tạp hơn một chút. Sự thật là BF không đọc một ký tự mà là một byte (bất kể byte đó là gì). Để tôi cho bạn xem ví dụ:
Trong linux
$ printf ł
bản in:
ł
đó là nhân vật đánh bóng cụ thể. Ký tự này không được mã hóa bằng bảng mã ASCII. Trong trường hợp này, đó là mã hóa UTF-8, vì vậy nó thường chiếm nhiều hơn một byte trong bộ nhớ máy tính. Chúng tôi có thể chứng minh điều đó bằng cách tạo một kết xuất thập lục phân:
$ printf ł | hd
cho thấy:
00000000 c5 82 |..|
Các số 0 được bù đắp. 82
là c5
byte đầu tiên và là byte thứ hai đại diện ł
(theo thứ tự chúng ta sẽ đọc chúng). |..|
là biểu diễn đồ họa không thể thực hiện được trong trường hợp này.
Chà, nếu bạn chuyển ł
làm đầu vào cho chương trình BF của mình đọc byte đơn, bộ nhớ chương trình sẽ giống như sau:
...[0][0][*197*][0][0]...
Tại sao 197
? Số 197
thập phân tốt là c5
hệ thập lục phân. Có vẻ quen thuộc ? Tất nhiên. Đó là byte đầu tiên của ł
!
3. Đầu ra
Để in ký tự, bạn sử dụng dấu chấm .
Điều đó có nghĩa là: Giả sử chúng ta xử lý giá trị ô thực tế như mã ASCII thập phân, hãy in ký tự tương ứng với đầu ra chuẩn.
Vâng, hãy tưởng tượng bộ nhớ chương trình BF của bạn trông như thế nào:
...[0][0][*97*][0][0]...
Nếu bạn sử dụng toán tử dot (.) Bây giờ, những gì BF làm là in:
a
Vì a
mã thập phân trong ASCII là 97
.
Vì vậy, ví dụ chương trình BF như thế này (97 điểm cộng 2 dấu chấm):
+++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ..
Sẽ tăng giá trị của ô mà nó trỏ đến lên đến 97 và in nó ra 2 lần.
aa
4. Vòng lặp
Trong vòng lặp BF bao gồm vòng lặp bắt đầu [
và kết thúc vòng lặp ]
. Bạn có thể nghĩ nó giống như khi ở trong C / C ++, nơi điều kiện là giá trị ô thực tế.
Hãy xem chương trình BF dưới đây:
++[]
++
tăng giá trị ô thực tế lên hai lần:
...[0][0][*2*][0][0]...
Và []
giống như while(2) {}
, vì vậy nó là vòng lặp vô hạn.
Giả sử chúng ta không muốn vòng lặp này là vô hạn. Chúng tôi có thể làm ví dụ:
++[-]
Vì vậy, mỗi khi vòng lặp lặp lại, nó sẽ giảm giá trị ô thực tế. Khi giá trị ô thực tế là 0
vòng lặp kết thúc:
...[0][0][*2*][0][0]... loop starts
...[0][0][*1*][0][0]... after first iteration
...[0][0][*0*][0][0]... after second iteration (loop ends)
Hãy xem xét một ví dụ khác về vòng lặp hữu hạn:
++[>]
Ví dụ này cho thấy, chúng tôi chưa kết thúc vòng lặp tại ô mà vòng lặp đã bắt đầu:
...[0][0][*2*][0][0]... loop starts
...[0][0][2][*0*][0]... after first iteration (loop ends)
Tuy nhiên, đó là cách tốt để kết thúc nơi chúng ta bắt đầu. Tại sao ? Bởi vì nếu vòng lặp kết thúc một ô khác thì nó đã bắt đầu, chúng ta không thể giả định con trỏ ô sẽ ở đâu. Thành thật mà nói, cách làm này giúp giảm thiểu tình trạng thiểu não.