Goto
s được phổ biến rộng rãi trong khoa học máy tính và lập trình vì chúng dẫn đến mã rất không có cấu trúc.
Python [giống như hầu hết mọi ngôn ngữ lập trình hiện nay] đều hỗ trợ lập trình có cấu trúc điều khiển luồng sử dụng if/then/other, loop và chương trình con.
Chìa khóa để suy nghĩ theo cách có cấu trúc là hiểu cách thức và lý do tại sao bạn phân nhánh trên mã.
Ví dụ: hãy giả vờ Python có câu lệnh goto
và tương ứng label
shudder . Nhìn vào đoạn
mã sau. Trong đó nếu một số lớn hơn hoặc bằng 0, chúng tôi sẽ in nếu nó
number = input[]
if number < 0: goto negative
if number % 2 == 0:
print "even"
else:
print "odd"
goto end
label: negative
print "negative"
label: end
print "all done"
Nếu chúng ta muốn biết khi nào một đoạn mã được thực thi, chúng ta cần cẩn thận truy nguyên trong chương trình và kiểm tra xem nhãn đã được chuyển đến như thế nào - đó là điều không thể thực hiện được.
Ví dụ: chúng ta có thể viết lại như trên:
number = input[]
goto check
label: negative
print "negative"
goto end
label: check
if number < 0: goto negative
if number % 2 == 0:
print "even"
else:
print "odd"
goto end
label: end
print "all done"
Ở đây, có hai cách có thể để đến "điểm cuối" và chúng ta không thể biết cái nào được chọn. Khi các chương trình trở nên lớn, loại vấn đề này trở nên tồi tệ hơn và kết quả là mã spaghetti
Để so sánh, bên dưới là cách bạn sẽ viết chương trình này bằng Python:
number = input[]
if number >= 0:
if number % 2 == 0:
print "even"
else:
print "odd"
else:
print "negative"
print "all done"
Tôi có thể xem một dòng mã cụ thể và biết trong điều kiện nào nó được đáp ứng bằng cách truy ngược lại cây if/then/else
chặn nó. Ví dụ, tôi biết rằng dòng print "odd"
sẽ được chạy khi một [[number >= 0] == True] and [[number % 2 == 0] == False]
.
Về mặt kỹ thuật có thể thêm một câu lệnh 'goto' vào python với một số công việc. Chúng tôi sẽ sử dụng các mô-đun "dis" và "new", cả hai đều rất hữu ích để quét và sửa đổi mã byte python.
Ý tưởng chính đằng sau việc triển khai là trước tiên đánh dấu một khối mã là sử dụng các câu lệnh "goto" và "nhãn". Một trình trang trí "@goto" đặc biệt sẽ được sử dụng cho mục đích đánh dấu các chức năng "goto". Sau đó, chúng tôi quét mã đó cho hai câu lệnh này và áp dụng các sửa đổi cần thiết cho mã byte bên dưới. Tất cả điều này xảy ra tại thời gian biên dịch mã nguồn.
import dis, new
def goto[fn]:
"""
A function decorator to add the goto command for a function.
Specify labels like so:
label .foo
Goto labels like so:
goto .foo
Note: you can write a goto statement before the correspnding label statement
"""
labels = {}
gotos = {}
globalName = None
index = 0
end = len[fn.func_code.co_code]
i = 0
# scan through the byte codes to find the labels and gotos
while i dis.HAVE_ARGUMENT:
b1 = ord[fn.func_code.co_code[i]]
b2 = ord[fn.func_code.co_code[i+1]]
num = b2 * 256 + b1
if name == 'LOAD_GLOBAL':
globalName = fn.func_code.co_names[num]
index = i - 1
i += 2
continue
if name == 'LOAD_ATTR':
if globalName == 'label':
labels[fn.func_code.co_names[num]] = index
elif globalName == 'goto':
gotos[fn.func_code.co_names[num]] = index
name = None
i += 2
# no-op the labels
ilist = list[fn.func_code.co_code]
for label,index in labels.items[]:
ilist[index:index+7] = [chr[dis.opmap['NOP']]]*7
# change gotos to jumps
for label,index in gotos.items[]:
if label not in labels:
raise Exception["Missing label: %s"%label]
target = labels[label] + 7 # skip NOPs
ilist[index] = chr[dis.opmap['JUMP_ABSOLUTE']]
ilist[index + 1] = chr[target & 255]
ilist[index + 2] = chr[target >> 8]
# create new function from existing function
c = fn.func_code
newcode = new.code[c.co_argcount,
c.co_nlocals,
c.co_stacksize,
c.co_flags,
''.join[ilist],
c.co_consts,
c.co_names,
c.co_varnames,
c.co_filename,
c.co_name,
c.co_firstlineno,
c.co_lnotab]
newfn = new.function[newcode,fn.func_globals]
return newfn
if __name__ == '__main__':
@goto
def test1[]:
print 'Hello'
goto .the_end
print 'world'
label .the_end
print 'the end'
test1[]
Hy vọng điều này trả lời câu hỏi.
7 hữu ích 0 bình luận chia sẻ