0% found this document useful (0 votes)
88 views

Turing Machines: Eric Martin, CSE, UNSW COMP9021 Principles of Programming

A Turing Machine (TM) is an idealized model of computation with infinite memory represented as a tape of cells containing 0s and 1s. To represent a finite section of the tape, a Python list containing 0s and 1s can be used. Functions can be defined to operate on and return values from lists, with return statements specifying the returned value. Strings can represent sequences of characters delimited by single or double quotes, with escaping of quotes within the string.

Uploaded by

Duan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
88 views

Turing Machines: Eric Martin, CSE, UNSW COMP9021 Principles of Programming

A Turing Machine (TM) is an idealized model of computation with infinite memory represented as a tape of cells containing 0s and 1s. To represent a finite section of the tape, a Python list containing 0s and 1s can be used. Functions can be defined to operate on and return values from lists, with return statements specifying the returned value. Strings can represent sequences of characters delimited by single or double quotes, with escaping of quotes within the string.

Uploaded by

Duan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 32

Turing machines

Eric Martin, CSE, UNSW


COMP9021 Principles of Programming

A Turing Machine (TM) is an idealised model of a computer. Whereas real computers have finite
memory, a TM has infinite memory, represented as a tape of consecutive cells, each of which contains
either the symbol 0 or the symbol 1, that extends infinitely both left and right. To represent a finite
section of the tape of a TM, a particular kind of object, namely, a Python list, is appropriate.
We just need to define a Python list whose elements are the integers 0 and 1, that Python also
represents as objects. We can then let a variable, say tape, denote what the list literal [0, 0, list表示
1, 1, 1, 0] itself denotes, namely, a sequence of bits stored in computer memory meant to be type
interpreted as the Python list that consists of the integers 0, 0, 1, 1, 1 and 0. This is a particular
kind of statement: an assignment. It involves two expressions: the list literal and the variable,
the list literal itself involving many occurrences of two expressions: 0 and 1. Expressions are
要计算表达式to be evaluated; the result of the evaluation is a mathematical entity, the expression’s value.
Statements are to be executed. In the following cell, on the line that precedes the statement
tape = [0, 0, 1, 1, 1, 0] and on the line of that statement, following it, are two comments.
When running the cell, tape = [0, 0, 1, 1, 1, 0] is executed. Then tape is evaluated and a
“standard” expression is output whose value is the same as tape’s value; that expression happens
to be the list literal that tape has been assigned to:
[1]: # 6 consecutive cells of a TM
tape = [0, 0, 1, 1, 1, 0] # Some cells contain 0, others contain 1
tape

[1]: [0, 0, 1, 1, 1, 0]

To more conveniently create a list with a repeating pattern (e.g., a list consisting of nothing but 0’s,
or a list consisting of nothing but 1’s, or a list where 0’s and 1’s alternate), one can use the * binary
operator. When both operands are numbers, * is multiplication. When running the following cell,
the expression 3*5 evaluates to the number 15, and 15, a more “standard” expression that also
evaluates to 15, is output:
[2]: 3 * 5

[2]: 15
操作对象 复制
When one operand is a list L and the other operand is a positive integer n, * duplicates L n
many times. Again, “standard” expressions are output whose values are the same as those of the
expressions that make up all lines in the following cell, the comment being ignored:
标准”表达式的输出值与组成下一单元格中所有行的表达式的值相同,注释被忽略

1
[3]: [0] * 0 # The empty list
1 * [0]
[1] * 7
5 * [0, 1]

[3]: []

[3]: [0]

[3]: [1, 1, 1, 1, 1, 1, 1]

[3]: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
索引 递增列举
One can refer to individual members of a list via their index, in relation to an increasing enumera-
tion of its elements from left to right, starting with 0, or in relation to a decreasing enumeration of
its elements from right to left, starting with -1. To illustrate this claim, we use the print() func-
tion. This function takes an arbitrary number of arguments, and outputs “standard” expressions
whose values are the same as those of the arguments; by default, in the output, two consecutive 连续的
调用
expressions are separated by a space. In both invocations of print() in the cell below, print()
receives 6 arguments. These invocations are statements, which are executed when running the cell,
and the arguments to print() are displayed as “standard” expressions with the same values as a
side effect: tape = [0, 0, 1, 1, 1, 0]
[4]: print(tape[0], tape[1], tape[2], tape[3], tape[4], tape[5])
print(tape[-1], tape[-2], tape[-3], tape[-4], tape[-5], tape[-6])

0 0 1 1 1 0
最后一个值的索引是列表的长度-1(因为索引从0开始)
0 1 1 1 0 0
非空的
So the positive index of the last element of a nonempty list is equal to the length of the list minus
1, and the negative index of the first element of a nonempty list is equal to minus the length of the
list. The length of a list is what the len() function returns when it receives (an expression that
evaluates to that) list as argument:

[5]: len(tape)

[5]: 6

Using an index which is at least equal to the length of the list generates an IndexError exception:
[6]: tape[len(tape)]

tape[6]没有值,所以返回错误

,→---------------------------------------------------------------------------

IndexError Traceback (most recent call␣


,→ last)

2
<ipython-input-6-57e58a86054f> in <module>
----> 1 tape[len(tape)]

IndexError: list index out of range

Using an index which is smaller than minus the length of the list also generates an IndexError
exception. The code in the following cell makes use of two operators: unary -, which negates its
unique operand, and binary -, which subtracts its second operand from its first operand, the former
operator taking precedence over the latter:
[7]: tape[-len(tape) - 1]

一元“-”对其唯一操作数求反,二元“-”从其第一个操作数减去其第二个操作数

,→---------------------------------------------------------------------------

IndexError Traceback (most recent call␣


last)
,→

<ipython-input-7-98a523e8b54d> in <module>
----> 1 tape[-len(tape) - 1]

IndexError: list index out of range


定义函数
def
Let us define a function to nicely display what tape represents on demand. To start with, we just
design the function. We let the body of the function definition consist of nothing but comments
that describe what the function is meant to do. Running the contents of the following cell shows
that Python does not accept this function definition as such (EOF is for End Of File):

[8]: def display_tape():


# Draw a horizontal line to represent
# the top boundary of the tape fragment.
# Draw the tape fragment's cell contents,
# also drawing vertical line segments as cell boundaries.
# Draw a horizontal line to represent
# the bottom boundary of the tape fragment.

File "<ipython-input-8-35410398860a>", line 7


# the bottom boundary of the tape fragment.
^
SyntaxError: unexpected EOF while parsing

3
The body of a function definition should consist of at least one statement. We can make pass
the only statement. The function definition is now acceptable (though it is only provisional, as we
have not implemented the function so that it can behave as specified by the comments in the
function body):

[9]: def display_tape():


# Draw a horizontal line to represent
# the top boundary of the tape fragment.
# Draw the tape fragment's cell contents,
# also drawing vertical line segments as cell boundaries.
# Draw a horizontal line to represent
# the bottom boundary of the tape fragment.
pass

When called, a function returns a value. An expression having that value, the function call itself
being such an expression, can be assigned to a variable. A return statement allows one to explicitly
let a function return a value: 明确地让函数返回一个值
[10]: def f():
return 2

f() # Returns 2, as shown when running the cell


x = f()
print(x)

[10]: 2

2 不执行return的函数返回None
A function that does not eventually execute a return statement still returns some value, namely,
the special value that the “standard” expression None evaluates to:
[11]: display_tape() # Returns None, which is not shown when running the cell
x = display_tape()
print(x)

None
A function can also explicitly return (the value that is the result of evaluating) None:

[12]: def f():


return None

f() # Returns None, which is not shown when running the cell
x = f()
print(x)

None
Getting back to the comments in display_tape()’s body, we see that we have to twice draw a line.

4
字符串文字可以用单引号分隔;反斜杠允许转义单引号并使其成为字符串的一部分:
连字符
This can be achieved by printing out a (representation of a) string of hyphens. More generally, a
string is a sequence of characters. String literals can be delimited with single quotes; a backslash
allows one to escape a single quote and make it part of the string:
[13]: string = 'A string with \' (single quote), not ", as delimiter'
string
print(string) 这里\'的作用是,不让反斜杠后面的单引号与A前的单引号连接
将单引号转义成字符串单引号的含义
[13]: 'A string with \' (single quote), not ", as delimiter'

A string with ' (single quote), not ", as delimiter


Alternatively, string literals can be delimited with double quotes; then double quotes, not single
quotes, need to be escaped to be part of the string:
[14]: string = "A string with \" (double quote), not ', as delimiter"
string
或者,字符串文字可以用双引号分隔;然后需要对双引号(而不是单引号)进行转义,
print(string)使其成为字符串的一部分:

[14]: 'A string with " (double quote), not \', as delimiter'

A string with " (double quote), not ', as delimiter


String literals can span many lines: just use either three single quotes or three double quotes as
delimiters. One could escape new line characters and define those string literals with single or
double quotes as delimiters, but they would not read as well:
三引号让字符串可以跨多行输出
[15]: string = '''A string containing both ' and "
with \''' (triple quote) as delimiter; 字符串文字可以跨越许多行:
it actually contains four single quotes, 只需使用三个单引号或三个双引号作为分隔符。
one of which is escaped''' 可以转义新行字符,并用单引号或双引号将
string 这些字符串文字定义为分隔符,
print(string) 但它们也不能读取

[15]: 'A string containing both \' and "\nwith \'\'\' (triple quote) as delimiter;\nit
actually contains four single quotes,\none of which is escaped'

A string containing both ' and "


with ''' (triple quote) as delimiter;
it actually contains four single quotes,
one of which is escaped

[16]: string = """


A string containing both ' and "
delimited with triple quotes
(observe: 4 spaces at the end of the previous line)
and containing five new line characters
"""
string

5
print(string)

[16]: '\nA string containing both \' and "\ndelimited with triple quotes
\n(observe: 4 spaces at the end of the previous line)\nand containing five new
line characters\n'

A string containing both ' and "


delimited with triple quotes
(observe: 4 spaces at the end of the previous line)
and containing five new line characters

The * operator can also be used with a string and a positive integer as operands:
[17]: # The empty string *号可以对字符串使用
'acb' * 0
1 * 'abc'
'abc' * 2
3 * 'abc'

[17]: ''

[17]: 'abc'

[17]: 'abcabc'

[17]: 'abcabcabc'
由于display_tape()必须两次绘制同一行,因此最好不要重复代码并定义一个辅助函数
Since display_tape() has to twice draw the same line, it is preferable not to duplicate
code and define an auxiliary function, say draw_horizontal_line(), to draw that line, and
let display_tape() call draw_horizontal_line() twice. As display_tape(), the function
draw_horizontal_line() takes no argument. In the function body, tape is used as a global
variable: tape has been declared outside the function body, but tape’s value can still be retrieved 恢复
in the function body. We define the function and then call it: tape用作全局变量
[18]: def draw_horizontal_line():
# multiplication takes precedence over addition
print('-' * (2 * len(tape) + 1))

draw_horizontal_line()

-------------
We can now partially implement display_tape(), removing the pass statement, and replacing the
first and last comments in its body with calls to draw_horizontal_line():
[19]: def display_tape():
draw_horizontal_line()

6
# Output cell contents, delimited with |'s.
draw_horizontal_line()

display_tape()

-------------
-------------
To complete the implementation of display_tape(), we need to write code that outputs a string
consisting of the characters '|', '0' and '1'. From tape, a (variable that evaluates to a) list
consisting of the integers (that are the values of) 0 and 1, we could obtain a corresponding list
consisting of the characters (that are the values of) '0' and '1', letting str() convert numbers to
列表推导式 strings, and making use of a list comprehension. The following expression reads as: the list of
all elements of the form str(symbol) where symbol ranges over tape, from beginning to end:
对于所有tape中的symbol,将每一个symbol转换成
[20]: [str(symbol) for symbol in tape]
字符串形式,以列表的形式输出
[20]: ['0', '0', '1', '1', '1', '0']

One could then use a particular function, the join() method of the str class, to create a string
from the (one character) strings in the former list. If join() is applied to the empty string, then
all those characters are “glued” together:
[21]: ''.join([str(symbol) for symbol in tape])
join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串
[21]: '001110' str.join(sequence) sequence是要连接的元素序列。
We could “glue” the characters with any other string:
[22]: '+A+'.join([str(symbol) for symbol in tape])

[22]: '0+A+0+A+1+A+1+A+1+A+0'

This is what we want:


[23]: '|'.join([str(symbol) for symbol in tape])

[23]: '0|0|1|1|1|0'

The previous expression is correct, but not as good as it could and should be. Indeed, the expression
is evaluated by processing all elements that make up (the list that is the value of) tape to create the
list (that is the value of) [str(symbol) for symbol in tape], and then processing all elements
that make up that second list to create the desired string. A better option is to use a generator
expression:
[24]: (str(symbol) for symbol in tape)

[24]: <generator object <genexpr> at 0x10fe900b0>

7
实现序列并逐个检索其每个成员的一种方法是调用next()函数
A generator expression is a potential sequence of elements. One way to actualise the sequence and
retrieve each of its members, one by one, is to call the next() function; when all elements have
been retrieved, a new call to next() generates a StopIteration exception:
[25]: E = (str(symbol) for symbol in tape) next() 返回迭代器的下一个项目。
next(E) next() 函数要和生成迭代器的iter() 函数一起使用
next(E)
next(E)
next(E)
next(E)
next(E)
next(E)
next(E)

[25]: '0'

[25]: '0'

[25]: '1'

[25]: '1'

[25]: '1'

[25]: '0'


---------------------------------------------------------------------------
,→

StopIteration Traceback (most recent call␣


last)
,→

<ipython-input-25-3b2a6685d829> in <module>
6 next(E)
7 next(E)
----> 8 next(E)
9 next(E)

StopIteration:

Any (expression that evaluates to a) generator expression can be passed as an argument to list()
which behind the scene, calls next() until StopIteration is generated, letting it gracefully com-
plete the construction of the list:

8
[26]: # The full syntax would be list((str(symbol) for symbol in tape)),
# but Python lets us simplify it and omit one pair of parentheses.
list(str(symbol) for symbol in tape)

[26]: ['0', '0', '1', '1', '1', '0']

join() also accepts a generator expression rather than a list as argument. In the code below,
join() processes the '0'’s and '1'’s it receives from the generator expression (str(symbol) for
symbol in tape), as that generator expression processes the 0’s and 1’s that make up the list
tape. The desired string is created “on the fly”; no intermediate list is created:
[27]: '|'.join(str(symbol) for symbol in tape) 不用创建中间列表,for in 列表生成式,str转义,
join连接
[27]: '0|0|1|1|1|0'

We also want to display a vertical bar at both ends. This can be done by concatenating three
strings into one. For that purpose, one can use the + binary operator. When both operands are
numbers, + is addition, but when both operands are strings, + is concatenation:
[28]: 'ABC' + 'DEF'

[28]: 'ABCDEF' 当两个操作数都是数字时,+表示加法,但当两个操作数都是字符串时,+表示串联:

+ is left associative. Therefore, in the following statement, the first occurrence of + creates a new
string S from its operands, and the second occurrence of + creates a new string from S and its
second operand:
[29]: '|' + '|'.join(str(symbol) for symbol in tape) + '|'

[29]: '|0|0|1|1|1|0|' 使用可选的关键字参数分隔符sep


将分隔符从默认值(空格)更改为空字符串
Since our aim is only to display a sequence of characters, we do not need to create a new string
from three strings: we can instead let print() take those three strings as arguments and print
them out changing the separator from the default, a space, to an empty string, using the optional
keyword only parameter sep to print(). Compare:
[30]: print('|', '|'.join(str(symbol) for symbol in tape), '|')
print('|', '|'.join(str(symbol) for symbol in tape), '|', sep='')

| 0|0|1|1|1|0 |
|0|0|1|1|1|0|
We now have the full implementation of display_tape():
[31]: def display_tape():
draw_horizontal_line()
print('|', '|'.join(str(e) for e in tape), '|', sep='')
draw_horizontal_line()

9
display_tape()

-------------
|0|0|1|1|1|0|
-------------
一个读写头,读写头可以在存储带上左右移动,并可以读、修改存储格上的数字或符号
Besides a tape, a TM has a head, that can be positioned below one of the cells that make up the
tape, thereby revealing its contents to the TM. A TM does not have a global view of the tape, it
only has a very local view, that of a single cell, but the contents of that cell changes over time as
the TM moves its head left or right. A TM also has a program to perform some computation. We
assume that before computation starts, the tape has been “initialised” in such a way that only a
finite number of cells contain the symbol 1, some cell contains 1, and the head is positioned below
the cell that contains the leftmost 1. The section of the tape that spans between the cells that store
the leftmost and rightmost 1’s is meant to encode some data (numbers, text, images, videos…).
At any stage of the computation, including before it starts and when it ends, if it ever
comes to an end, the TM is in one of a finite number of states. The program of a
TM consists of a finite set of instructions, each instruction being a quintuple of the form
(state, symbol, new_state, new_symbol, direction), with the following intended meaning: if the cur-
rent state of the TM is state, and if the head of the TM is currently positioned under a cell that
stores symbol, then the contents of that cell becomes new_symbol (which can be the same as
symbol), the state of the TM becomes new_state (which can be the same as state), and the head of
the TM moves one cell to the right or one cell to the left depending on whether direction is R or L.
A TM is deterministic. This means that at any stage, at most one instruction can be executed: its
program does not have two distinct instructions that both start with the same first two elements,
with the same state and symbol. Computation runs for as long as some instruction can be executed.
Either there is always such an instruction, in which case computation never terminates, or at some
stage no instruction can be executed, in which case computation ends.
For illustration purposes, assume that tape is set to [0, 0, 1, 1, 1, 1, 0] and represents a
segment of the tape that contains all 1’s on the tape. Suppose that the head of the TM is positioned
below the cell that stores the leftmost 1, corresponding to the member of tape of index 2, as it
is meant to be before computation starts. Suppose that there are two possible states, green and
blue. Also suppose that the initial state, that is, the state of the TM before computation starts,
is green. If the program of the TM has no instruction whose first two members are green and 1,
then computation stops. One the other hand, if the program has one such instruction, then that
instruction is one of the following 8 instructions, and the TM executes it:
• (green, 1, green, 1, R): the state of the TM remains green, tape is left unchanged, and the
head of the TM moves right (so positions itself below the cell that corresponds to 1 at index
3 in tape).
在计算的任何阶段,包括开始前和结束时,如果它结束了,TM处于有限状态之一。TM程序由一组有限的指令组成,
• (green, 1, green, 1, L): the state of the TM remains green, tape is left unchanged, and the
每条指令是形式(状态;符号;新状态;新符号;方向)的五倍,其目的是:
head of the TM moves left (so positions itself below the cell that corresponds to 0 at index 1
如果TM的当前状态是状态,并且TM的头部当前位于存储符号的单元下,
in tape).
则TM的内容单元变为新的_符号(可以与符号相同),TM的状态变为新的_状态(可以与状态相同),
• (green, 1, green, 0, R): the state of the TM remains green, the 1 in tape at index 2 is changed
TM的头部将一个单元向右移动或将一个单元向左移动,这取决于方向是R还是L。TM是确定的。
to 0, and the head of the TM moves right (so positions itself below the cell that corresponds
这意味着在任何阶段,最多只能执行一条指令:其程序没有两条不同的指令,
to 1 at index 3 in tape).
它们都以相同的前两个元素开头,具有相同的状态和符号。只要某些指令可以执行。
• (green, 1, green, 0, L): the state of the TM remains green, the 1 in tape at index 2 is changed
或者总有这样一条指令,在这种情况下,计算永远不会终止,或者在某个阶段,
to 0, and the head of the TM moves left (so positions itself below the cell that corresponds
不能执行任何指令,在这种情况下,计算结束
为了便于说明,假设磁带设置为[0,0,1,1,1,1,0],并表示磁带中包含磁带上所有1的一段。
假设TM的头部位于存储最左边1的单元格的下方,对应于索引2的磁带成员,就像在计算开始之前一样。
10
假设有两种可能的状态,绿色和蓝色。还假设初始状态,即计算开始前TM的状态为绿色。
如果TM程序没有前两个成员为绿色和1的指令,则计算停止。另一方面,如果程序有这样一条指令,
则该指令是以下8条指令之一,并且TM执行它:
-(绿色,1,绿色,1,R):TM的状态保持绿色,磁带保持不变,TM的磁头向右移动
(因此将自身定位在磁带索引3处与1对应的单元格下方)。
-(green,1,green,1,L):TM的状态保持为绿色,磁带保持不变,TM的磁头向左移动
(因此将自身定位在磁带索引1处对应于0的单元格下方)。
-(绿色,1,绿色,0,R):TM的状态保持绿色,索引2处的磁带中的1变为0,TM的磁头向右移动
to 0 at index 1 in tape).
(因此,将其自身定位在磁带中索引3处与1对应的单元格下方)。
• (green, 1, blue, 1, R): the state of the TM changes to blue, tape is left unchanged, and the
-(green,1,green,0,L):TM的状态保持为绿色,索引2处的磁带中的1变为0,TM的磁头向左移动
head of the TM moves right (so positions itself below the cell that corresponds to 1 at index
(因此将自身定位在磁带中索引1处对应于10到0的单元下方)。
3 in tape).
-(绿色,1,蓝色,1,R):TM的状态变为蓝色,磁带保持不变,TM的头部向右移动
• (green, 1, blue, 1, L): the state of the TM changes to blue, tape is left unchanged, and the
(因此将自身定位在磁带索引3处与1对应的单元格下方)。
head of the TM moves left (so positions itself below the cell that corresponds to 0 at index 1
-(绿色,1,蓝色,1,L):TM的状态变为蓝色,磁带保持不变,TM的磁头向左移动
in tape).
(因此将自身定位在磁带索引1处0对应的单元格下方)。
• (green, 1, blue, 0, R): the state of the TM changes to blue, the 1 in tape at index 2 is changed
-(绿色,1,蓝色,0,R):TM的状态变为蓝色,索引2处的磁带中的1变为0,TM的磁头向右移动
to 0, and the head of the TM moves right (so positions itself below the cell that corresponds
(因此将自身定位在磁带中索引3处与1对应的单元格下方)。
to 1 at index 3 in tape).
-(绿色,1,蓝色,0,L):TM的状态变为蓝色,索引2处的磁带中的1变为0,TM的磁头向左移动
• (green, 1, blue, 0, L): the state of the TM changes to blue, the 1 in tape at index 2 is changed
(因此将自身定位在磁带中索引1处对应于0的单元格下方)。
to 0, and the head of the TM moves left (so positions itself below the cell that corresponds
直觉上,一个状态捕捉到了一些过去的存储。如果有无限多的状态可用,
to 0 at index 1 in tape).
就有可能记住自计算开始以来发生的一切。由于只有有限多个状态可用,
Intuitively, a state captures some memory of the past. If infinitely many states were available,
状态通常只能跟踪发生的部分情况;例如,人们通常记不起自计算开始以来有多少1被0覆盖,
it would be possible to remember everything that happened since computation started. As only
但有限多的状态足以记住该数字是偶数还是奇数。
finitely many states are available, states can usually keep track of only part of what happened;
for instance, one usually cannot remember how many 1’s have been overwritten with 0’s since
computation started, but finitely many states are enough to remember whether that number is
even or odd.
We provide sample TM programs to compute various functions from N∗ to N, or from N∗ × N∗
to N (N denotes the set of natural numbers, and N∗ the set of strictly positive natural numbers):
• the successor function, that maps n to n + 1 N表示自然数的集合,N*表示正整数的集合
• the parity function, that maps n to 0 if n is even, and to 1 otherwise
• division by 2, that maps n to b n2 c
• addition
• multiplication
For the first three programs, data is for a single nonzero natural number, encoded in unary: 1 is
encoded as 1, 2 as 11, 3 as 111, 4 as 1111… For the last two programs, data is for two nonzero
natural numbers, both encoded in unary, and separated by a single 0.
For all programs, when computation stops, data is “erased” and the tape just stores the natural
number r that is the result of the computation, represented in unary.
• If r > 0, the head of the TM is positioned under the cell that stores the leftmost 1.
• If r = 0, the tape contains nothing but 0’s and the head is positioned anywhere on the tape.
These TM programs are saved in the files:
• successor.txt
• parity.txt
• division_by_2.txt
• addition.txt
• multiplication.txt
turing_machine_simulator.py创建一个小部件来试验这些程序和其他程序
The program turing_machine_simulator.py creates a widget to experiment with those programs
and others. It has a Help menu. Rather than representing the head of the TM in one way or
another, it identifies the cell that the head is currently positioned under by displaying its contents
in boldface red.
它不是以某种方式表示TM的头部,而是通过以黑体红色显示其内容来标识头部当前所在的单元格
11
open()函数用于打开一个文件,创建一个file对象
with open('filename') as rename打开文件并给它一个名称用于调用
Let us now examine how to process the contents of a file containing the program of a TM, say
division_by_2.txt. The open() function returns a handle to a file, which we can make the value
of a variable on which we can then operate to read and extract the contents of the file, before
eventually closing it. open() expects to be given as argument a string that represents the location
of the file. Using the name of the file for the string makes the location relative to the working
directory, which is fine here since division_by_2.txt is indeed stored in the working directory.
By default, open() works in reading mode; this is appropriate since we do not want to overwrite
or modify division_by_2.txt:
[32]: TM_program_file = open('division_by_2.txt')
TM_program_file
# Operate on TM_program_file to read and process
# the contents of division_by_2.txt.
TM_program_file.close()

[32]: <_io.TextIOWrapper name='division_by_2.txt' mode='r' encoding='UTF-8'>

The previous syntax is simple, but it is preferable to opt for an alternative and use a context
manager and an associate with ... as expression, that gracefully closes the file after it has been
processed, or earlier in case problems happen during processing:
[33]: with open('division_by_2.txt') as TM_program_file:
TM_program_file
# Operate on TM_program_file to read and process
# the contents of division_by_2.txt

[33]: <_io.TextIOWrapper name='division_by_2.txt' mode='r' encoding='UTF-8'>

The contents of the file can be extracted all at once, as a list of strings, one string per line in the
file:
[34]: with open('division_by_2.txt') as TM_program_file:
TM_program_file.readlines()
readlines()方法用于读取所有行(直到结束符 EOF)并返回列表
[34]: ['# Initial state: del1\n', fileObject.readlines( );
'\n',
'del1 1 del2 0 R\n',
'del2 1 mov1R 0 R\n',
'mov1R 1 mov1R 1 R\n',
'mov1R 0 mov2R 0 R\n',
'mov2R 1 mov2R 1 R\n',
'mov2R 0 mov1L 1 L\n',
'mov1L 1 mov1L 1 L\n',
'mov1L 0 mov2L 0 L\n',
'mov2L 1 mov2L 1 L\n',
'mov2L 0 del1 0 R\n',
'del1 0 end 0 R\n',
'del2 0 end 0 R\n']

12
When dealing with very large files, storing the whole file contents as a list of strings can be too
ineffective. Instead, lines can be read one by one on demand with the readline() method:
[35]: with open('division_by_2.txt') as TM_program_file:
TM_program_file.readline()
TM_program_file.readline()
TM_program_file.readline()
TM_program_file.readline()

[35]: '# Initial state: del1\n'

[35]: '\n'

[35]: 'del1 1 del2 0 R\n'

[35]: 'del2 1 mov1R 0 R\n'


迭代器
In fact, open() returns an iterator, that is, an object of the same type as a generator expression,
which next() can be applied to; essentially, readline() is just alternative syntax for next():
[36]: with open('division_by_2.txt') as TM_program_file:
next(TM_program_file)
next(TM_program_file)
next(TM_program_file)
next(TM_program_file)

[36]: '# Initial state: del1\n'

[36]: '\n'

[36]: 'del1 1 del2 0 R\n'

[36]: 'del2 1 mov1R 0 R\n'


迭代器通常最好用for语句处理
Iterators are usually best processed with a for statement, a kind of loop. Behind the scene, for
calls next() until the latter generates a StopIteration exception, which lets it gracefully exit the
loop. Note that since every line of the file being processed yields a string that ends in a new line
character, and print() “goes to the next line” by default, that is, outputs a new line character,
the output of the following code fragment shows one blank line between two consecutive lines:
[37]: with open('division_by_2.txt') as TM_program_file:
for line in TM_program_file: 以下代码片段的输出在两个连续行之间显示一个空行:
print(line)

# Initial state: del1


del删除将1改成0
mov移动但不更改值

del1 1 del2 0 R

13
del2 1 mov1R 0 R

mov1R 1 mov1R 1 R

mov1R 0 mov2R 0 R

mov2R 1 mov2R 1 R

mov2R 0 mov1L 1 L

mov1L 1 mov1L 1 L

mov1L 0 mov2L 0 L

mov2L 1 mov2L 1 L

mov2L 0 del1 0 R

del1 0 end 0 R

del2 0 end 0 R
把print()里的end默认为空格,即end=' ',设置为end='',即不空行
These blank lines can be eliminated from the output by changing the value of the keyword only
parameter end of print() from the default new line character to an empty string:
[38]: with open('division_by_2.txt') as TM_program_file:
for line in TM_program_file:
print(line, end='')

# Initial state: del1

del1 1 del2 0 R
del2 1 mov1R 0 R
mov1R 1 mov1R 1 R
mov1R 0 mov2R 0 R
mov2R 1 mov2R 1 R
mov2R 0 mov1L 1 L
mov1L 1 mov1L 1 L
mov1L 0 mov2L 0 L
mov2L 1 mov2L 1 L
mov2L 0 del1 0 R
del1 0 end 0 R
del2 0 end 0 R
We are only interested in lines that represent instructions, neither in lines that represent a comment
nor by blank lines. For the former, the startswith() method of the str class is useful. It returns
one of both Boolean values True and False:

14
startswith() 方法用于检查字符串是否是以指定子字符串开头,
如果是则返回 True,否则返回 False。如果参数 beg 和 end 指定值,则在指定范围内检查。
str.startswith(str, beg=0,end=len(string))

[39]: 'A string'.startswith('')


'A string'.startswith('A')
'A string'.startswith('A ')
'A string'.startswith('A s')
'A string'.startswith('a')

[39]: True

[39]: True

[39]: True

[39]: True
isspace() 方法检测字符串是否只由空格组成
[39]: False str.isspace()
For the latter, the isspace() method of the str class is useful:
[40]: ''.isspace()
' a'.isspace()
' '.isspace()
' '.isspace()
# \t is for tab
' \t \n'.isspace()
'''

'''.isspace()

[40]: False

[40]: False

[40]: True

[40]: True

[40]: True

[40]: True

Using startswith() and isspace() as part of a Boolean expression that makes up the condi-
tion of an if statement, a kind of test, whose body is executed if and only if the condition evaluates
to (the value of the “special” expression) True rather than (the value of the “special” expression)
False, we can output only lines that represent instructions. The Boolean expression makes use of
two logical operators, namely, not and and, for negation and conjunction, respectively:

15
[41]: with open('division_by_2.txt') as TM_program_file:
for line in TM_program_file:
if not line.startswith('#') and not line.isspace():
print(line, end='')

del1 1 del2 0 R
del2 1 mov1R 0 R
mov1R 1 mov1R 1 R
mov1R 0 mov2R 0 R
mov2R 1 mov2R 1 R
mov2R 0 mov1L 1 L
mov1L 1 mov1L 1 L
mov1L 0 mov2L 0 L
mov2L 1 mov2L 1 L
mov2L 0 del1 0 R
del1 0 end 0 R
del2 0 end 0 R
Alternatively, we can test the negation of the condition of the if statement in the previous code
fragment (applying one of de Morgan’s laws to get a disjunction from a conjunction as well as
applying double negation elimination) and use a continue statement not to process any further
any line that is not of interest. The Boolean expression then makes use of the logical operator or,
for (inclusive as opposed to exclusive) disjunction.

[42]: with open('division_by_2.txt') as TM_program_file:


for line in TM_program_file:
if line.startswith('#') or line.isspace():
continue
print(line, end='')

del1 1 del2 0 R
del2 1 mov1R 0 R
mov1R 1 mov1R 1 R
mov1R 0 mov2R 0 R
mov2R 1 mov2R 1 R
mov2R 0 mov1L 1 L
mov1L 1 mov1L 1 L
mov1L 0 mov2L 0 L
mov2L 1 mov2L 1 L
mov2L 0 del1 0 R
del1 0 end 0 R
split() 通过指定分隔符对字符串进行切片
del2 0 end 0 R
str.split(str="", num=string.count(str))
After an instruction has been retrieved, it is necessary to isolate its 5 components. The split()
method of the str class is useful. We can pass to split() a nonempty string as argument:
[43]: 'aXaaXaaaXaaaXaaXaX'.split('a')
'aXaaXaaaXaaaXaaXaX'.split('aa')
'aXaaXaaaXaaaXaaXaX'.split('aaa')

16
'aXaaXaaaXaaaXaaXaX'.split('aaaa')

[43]: ['', 'X', '', 'X', '', '', 'X', '', '', 'X', '', 'X', 'X']

[43]: ['aX', 'X', 'aX', 'aX', 'XaX']

[43]: ['aXaaX', 'X', 'XaaXaX']

[43]: ['aXaaXaaaXaaaXaaXaX']
如果没有参数传递给split(),则任何最长的空格字符序列都将扮演分隔符的角色,
并且任何前导或尾随的空格字符序列都将被忽略:
If no argument is passed to split(), then any longest sequence of space characters will play the
role of separator, and any leading or trailing sequence of space characters will be ignored:
[44]: ' \n\t X X\tX\nX \t \n X'.split()

[44]: ['X', 'X', 'X', 'X', 'X']

So we can now get from each instruction a list of 5 strings:


[45]: with open('division_by_2.txt') as TM_program_file:
for line in TM_program_file:
if line.startswith('#') or line.isspace():
continue
print(line.split())

['del1', '1', 'del2', '0', 'R']


['del2', '1', 'mov1R', '0', 'R']
['mov1R', '1', 'mov1R', '1', 'R']
['mov1R', '0', 'mov2R', '0', 'R']
['mov2R', '1', 'mov2R', '1', 'R']
['mov2R', '0', 'mov1L', '1', 'L']
['mov1L', '1', 'mov1L', '1', 'L']
['mov1L', '0', 'mov2L', '0', 'L']
['mov2L', '1', 'mov2L', '1', 'L']
['mov2L', '0', 'del1', '0', 'R']
['del1', '0', 'end', '0', 'R'] (state; symbol)表示可能的配置
['del2', '0', 'end', '0', 'R'] (什么可能是当前状态和存储在TM头部当前所在单元中的符号)
A list of 5 strings is not the best representation of an instruction. Recall that since a TM is
deterministic, no two distinct instructions share the same first two elements. A good way to put it
is that the program of a TM is a function that maps a pair of the form (state, symbol) to a triple
of the form (new_state, new_symbol, direction): (state, symbol) represents a possible configuration
(what is possibly the current state and the symbol stored in the cell that the head of the TM
is currently positioned under), while (new_state, new_symbol, direction) represents what to do in
case the computation is at a stage when that possible configuration happens to be the current one.
To represent functions, mappings, Python offers dictionaries, that associate values to keys:
字典,键值对
d = {key1 : value1, key2 : value2 }

17
[46]: # An empty dictionary
{}
# A dictionary with 4 keys, mapping names of digits to digits.
{'one': 1, 'two': 2, 'three': 3, 'four': 4}
# A dictionary with 4 keys, mapping English words to German translations
{'tap': 'Hahn', 'table': 'Tisch', 'rooster': 'Hahn', 'train': 'Zug'}
# A dictionary with 3 keys, mapping German words to English translations
{'Hahn': ['tap', 'rooster'], 'Tisch': ['table'], 'Zug': ['train']}

[46]: {}

[46]: {'one': 1, 'two': 2, 'three': 3, 'four': 4}

[46]: {'tap': 'Hahn', 'table': 'Tisch', 'rooster': 'Hahn', 'train': 'Zug'}

[46]: {'Hahn': ['tap', 'rooster'], 'Tisch': ['table'], 'Zug': ['train']}

Observe how we mapped an English word to a German word, but a German word to a list of English
words: this is because the English words under consideration all have a single German translation,
whereas some of the German words under consideration have more than one English translation.
Getting back to our TM instructions, we could think of creating a dictionary TM_program that
would have for each instruction of the form (state, symbol, new_state, new_symbol, direction), the
list [state, symbol] as a key and the list [new_state, new_symbol, direction] as value for that key.
That does not work:
[47]: {['del1', '1']: ['del2', '0', 'R']}


,→---------------------------------------------------------------------------

TypeError Traceback (most recent call␣


,→last)

<ipython-input-47-384215f30c9d> in <module>
----> 1 {['del1', '1']: ['del2', '0', 'R']}

TypeError: unhashable type: 'list'

The keys of a dictionary should be immutable objects, that is, objects that cannot change. It is
possible to change a dictionary by adding or removing a key together with its associated value, but
an existing key cannot be modified. A list is mutable, as it can be changed in many ways. It is
for instance possible to change one of the members of a list:
[48]: L = [10, 11, 12]
L[1] = 21

列表list
18
L

[48]: [10, 21, 12]


append() 方法用于在列表末尾添加新的对象。
It is possible to add elements to a list: list.append(obj)
[49]: L = [10, 11, 12]
L.append(13)
L

[49]: [10, 11, 12, 13]

It is possible to remove elements from a list: remove() 函数用于移除列表中某个值的第一个匹配项


list.remove(obj)
[50]: L = [10, 11, 12]
L.remove(11)
L

[50]: [10, 12]

Tuples offer alternatives to lists to define sequences of values. Tuple literals are surrounded by
parentheses rather than by square brackets. In many contexts, the parentheses are optional as
commas are all what is needed to define a tuple:
[51]: # The empty tuple
()
(10,)
10,
(10, 11)
10, 11
(10, 11, 12)
10, 11, 12
# Not a tuple, but 10 surrounded by parentheses
(10)

[51]: ()
元组与列表类似,不同之处在于元组的元素不能修改(包括删除添加)。
[51]: (10,) 元组使用小括号,列表使用方括号。
元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可
[51]: (10,)
创建空元组
[51]: (10, 11) tup1 = ()

[51]: (10, 11)

[51]: (10, 11, 12)

[51]: (10, 11, 12)

19
[51]: 10

It is not possible to change the value of one of the members of a tuple:


[52]: T = 10, 11, 12
T[1] = 21


,→---------------------------------------------------------------------------

TypeError Traceback (most recent call␣


,→last)

<ipython-input-52-0c5661d0cf37> in <module>
1 T = 10, 11, 12
----> 2 T[1] = 21

TypeError: 'tuple' object does not support item assignment

It is not possible to add or remove elements to or from a tuple: there is no append nor remove
method for tuples. Tuples can be dictionary keys. Dictionary values can be lists or tuples, only
the keys should be immutable:
不可变的
[53]: {('del1', '1'): ['del2', '0', 'R']}
{('del1', '1'): ('del2', '0', 'R')}

[53]: {('del1', '1'): ['del2', '0', 'R']}

[53]: {('del1', '1'): ('del2', '0', 'R')}

Let us opt for representing the program of a TM as a dictionary where both keys and values are
tuples. One can start with an empty dictionary and add a new key and its associated value every
time a new instruction is processed:
[54]: TM_program = {}
with open('division_by_2.txt') as TM_program_file:
for line in TM_program_file:
if line.startswith('#') or line.isspace():
continue
instruction = line.split()
# Simplified syntax, without parentheses for keys and values.
# The full syntax would be be:
# TM_program[(instruction[0], instruction[1])] =\
# (instruction[2], instruction[3], instruction[4])
# \ used for line continuation

20
instruction是从文件中读取的一行元素
instruction[0],instruction为键,2,3,4为值

TM_program[instruction[0], instruction[1]] =\
instruction[2], instruction[3], instruction[4]
TM_program

[54]: {('del1', '1'): ('del2', '0', 'R'),


('del2', '1'): ('mov1R', '0', 'R'),
('mov1R', '1'): ('mov1R', '1', 'R'),
('mov1R', '0'): ('mov2R', '0', 'R'),
('mov2R', '1'): ('mov2R', '1', 'R'),
('mov2R', '0'): ('mov1L', '1', 'L'),
('mov1L', '1'): ('mov1L', '1', 'L'),
('mov1L', '0'): ('mov2L', '0', 'L'),
('mov2L', '1'): ('mov2L', '1', 'L'),
('mov2L', '0'): ('del1', '0', 'R'),
('del1', '0'): ('end', '0', 'R'),
('del2', '0'): ('end', '0', 'R')}
Python允许我们将列表或元组的每个元素分配给列表或相同长度元组的每个对应元素
The previous code fragment is not as readable as it can be. Python lets us assign each element of
a list or a tuple to each of the corresponding elements of a list or a tuple of the same length:
[55]: [a1, b1, c1] = [10, 11, 12]
# Simplified syntax for what could also be written as
# (a2, b2, c2) = (21, 22, 23)
# or
# a2, b2, c2 = (21, 22, 23)
# or
# (a2, b2, c2) = 21, 22, 23
a2, b2, c2 = 21, 22, 23
# Simplified syntax for what could also be written as
# [a3, b3, c3] = (31, 32, 33)
[a3, b3, c3] = 31, 32, 33
# Simplified syntax for what could also be written as
# (a4, b4, c4) = [41, 42, 43]
a4, b4, c4 = [41, 42, 43]

[a1, b1, c1, a2, b2, c2, a3, b3, c3, a4, b4, c4]
# Simplified syntax for what could also be written as
# (a1, b1, c1, a2, b2, c2, a3, b3, c3, a4, b4, c4)
a1, b1, c1, a2, b2, c2, a3, b3, c3, a4, b4, c4

[55]: [10, 11, 12, 21, 22, 23, 31, 32, 33, 41, 42, 43]

[55]: (10, 11, 12, 21, 22, 23, 31, 32, 33, 41, 42, 43)

Though functionally equivalent to what we wrote above, the following code fragment is more
readable:

21
[56]: TM_program = {}
with open('division_by_2.txt') as TM_program_file:
for line in TM_program_file:
if line.startswith('#') or line.isspace():
continue
state, symbol, new_state, new_symbol, direction = line.split()
# Simplified syntax, without parentheses for both keys and
# values. The full syntax would be be:
# TM_program[(state, symbol)] = (new_state, new_symbol, direction)
TM_program[state, symbol] = new_state, new_symbol, direction
TM_program

[56]: {('del1', '1'): ('del2', '0', 'R'), [state,symbol]为键


('del2', '1'): ('mov1R', '0', 'R'), new_state,new_symbol,direction为值
('mov1R', '1'): ('mov1R', '1', 'R'),
('mov1R', '0'): ('mov2R', '0', 'R'),
('mov2R', '1'): ('mov2R', '1', 'R'),
('mov2R', '0'): ('mov1L', '1', 'L'),
('mov1L', '1'): ('mov1L', '1', 'L'),
('mov1L', '0'): ('mov2L', '0', 'L'),
('mov2L', '1'): ('mov2L', '1', 'L'),
('mov2L', '0'): ('del1', '0', 'R'),
('del1', '0'): ('end', '0', 'R'),
('del2', '0'): ('end', '0', 'R')}

States and directions are naturally represented as strings. On the other hand, it would be simpler
and more natural to represent symbols as the integers 0 and 1, not as the strings '0' and '1', all
the more so that tape consists of 0’s and 1’s, not '0'’s and '1'’s. One can get the latter from the
former with int():
[57]: int('0')
int('1')
int('17')
int('-23')
状态和方向自然地表示为字符串。另一方面,将符号表示为整数0和1,
[57]: 0 而不是字符串“0”和“1”,会更简单、更自然,
这样磁带就由0和1组成,而不是“0”和“1”。
[57]: 1
可以使用int()从前者获取后者:状态和方向自然地表示为字符串。
另一方面,将符号表示为整数0和1,而不是字符串“0”和“1”,会更简单、更自然,
[57]: 17
这样磁带就由0和1组成,而不是“0”和“1”。
[57]: -23 可以使用int()从前者获取后者:

So we can improve our code further as follows:


[58]: TM_program = {}
with open('division_by_2.txt') as TM_program_file:
for line in TM_program_file:

22
if line.startswith('#') or line.isspace():
continue
state, symbol, new_state, new_symbol, direction = line.split()
TM_program[state, int(symbol)] =\
new_state, int(new_symbol), direction
TM_program

[58]: {('del1', 1): ('del2', 0, 'R'),


('del2', 1): ('mov1R', 0, 'R'),
('mov1R', 1): ('mov1R', 1, 'R'),
现在TM的程序已经以一种似乎适合进一步使用的形式被捕获,
('mov1R', 0): ('mov2R', 0, 'R'),
我们可以编写代码来模拟计算。回想一下,磁带只代表磁带的
('mov2R', 1): ('mov2R', 1, 'R'),有限部分。随着计算的进行,可能会对磁带越来越大的部分进行
('mov2R', 0): ('mov1L', 1, 'L'),探索,可能需要相应地向左或向右扩展磁带。这个小部件可以做到
('mov1L', 1): ('mov1L', 1, 'L'),这一点。在这里,为了简化我们的任务,我们假设磁带的定义是
('mov1L', 0): ('mov2L', 0, 'L'),这样的,它足够大,并且在两端包含足够多的0,使得TM的头部
('mov2L', 1): ('mov2L', 1, 'L'),不超过磁带确定的部分,对于正在考虑的输入,因此,磁带中1的
('mov2L', 0): ('del1', 0, 'R'), 左右各有足够的0对输入进行编码,以便在磁带的边界内执行
('del1', 0): ('end', 0, 'R'), 所有指令。考虑到这一点,并且知道division by 2程序是
('del2', 0): ('end', 0, 'R')} 如何工作的,让我们按如下方式设置磁带,因此将输入设置为7:

Now that the program of the TM has been captured in a form that seems appropriate for further
use, we can write code to simulate computation. Recall that tape represents only a finite section of
the tape. As computation progresses, larger and larger sections of the tape are potentially explored,
possibly requiring to extend tape accordingly, left or right. The widget does this. Here, in order to
simplify our task, we assume that tape is defined in such a way that it is large enough and contains
enough 0’s at both ends for the head of the TM not to go beyond the section determined by tape,
for the input under consideration, so there are enough 0’s left and right of the 1’s in tape that
encode the input for all instructions to be executed within tape’s boundaries. With this in mind,
and knowing how the division by 2 program works, let us set tape as follows, so set for an input of
7:
[59]: tape = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
当计算结束时,从7/2 = 3开始磁带中应该只有3个连续的1
When computation ends, there should be only 3 consecutive 1’s in tape since b 72 c = 3. At any stage
of the computation, we need to know the current state and the head’s current position. Before
computation starts, the current state is the initial state, set to 'del1':
[60]: current_state = 'del1' 在计算中,我们需要知道当前的状态和头部的当前位置。以前
作为一个指针 计算开始,当前状态是初始状态,设置为“del1”:
The head is supposed to be positioned below the cell of the tape that contains the leftmost 1. The
index() method of the str class makes it easy to determine that position within tape:
起始位置 磁头应该位于最左边值为1的磁带单元下面。
[61]: current_position = tape.index(1) str类的index()方法便于确定磁带中的位置:
current_position index() 方法检测字符串中是否包含子字符串str
如果指定 beg(开始)和 end(结束)范围,
[61]: 3 则检查是否包含在指定范围内
str.index(str, beg=0, end=len(string))
We know how to find out the contents of the cell which the head is currently positioned under, that
is, the current symbol:

23
起始位置的值
[62]: current_symbol = tape[current_position]
current_symbol

[62]: 1

At this stage, is there a (unique) instruction to execute? Yes if and only if TM_program has
(current_state, current_symbol) as one of its keys:
keys() 方法返回一个可迭代对象,
[63]: (current_state, current_symbol) in TM_program.keys() 可以使用 list() 来转换为列表
dict.keys()
[63]: True

Instead of asking whether a given object is one of the keys of a dictionary, one can more simply
ask whether the object is in the dictionary (as a key):

[64]: (current_state, current_symbol) in TM_program

[64]: True

One can then retrieve the rest of the instruction, its “to do” part:
[65]: new_state, new_symbol, direction = TM_program[current_state, current_symbol]
new_state
new_symbol ??????两个值赋给三个值
direction

[65]: 'del2'执行该指令意味着将tape[current_symbol]更改为new_symbol
(更准确地说,让tape[current_symbol]赋值给new_symbol,
[65]: 0 让tape[current_symbol]表示新符号表示的内存位),
将current_state从当前状态更改为new_state,
[65]: 'R' current_position对应的值根据方向是“R”还是“L”,加上或减去1,(R就+1,L就-1
并将current_symbol更改为存储在磁带索引处的值,该值是current_position的新值
Executing that instruction means changing tape[current_symbol] to new_symbol (more precisely,
letting tape[current_symbol] evaluate to the value that new_symbol evaluates to, by letting
tape[current_symbol] denote the bits in memory that new_symbol denotes), changing the current
state from current_state to new_state, changing current_position from the value it currently
has to that value to plus or minus 1 depending on whether direction is 'R' or 'L', respectively,
and changing current_symbol to the value stored in tape at the index which is the new value of
current_position.
To test whether direction is 'R' or 'L', it is useful to make use of equality, one of the 6
comparison operators:
[66]: # Equality
2 == 2, 2 == 3
# Inequality
'a' != 'b', 'a' != 'a'
# Less than (w.r.t. lexicographic order)
'abcd' < 'abe', 'z' < 'abc'

24
# Less than or equal to (w.r.t. lexicographic order)
[1, 2, 3] <= [1, 2, 3], [2] <= [1, 3]
# Greater than or equal to
False >= False, False > True
# Greater than (w.r.t. inclusion)
{1, 2, 3} > {1, 3}, {2, 3} > {1}

[66]: (True, False)

[66]: (True, False)

[66]: (True, False)

[66]: (True, False)

[66]: (True, False)

[66]: (True, False)


要给current_position赋值,使用if。。。else表达式。
To assign the appropriate value to current_position, it is useful to make use of an if ... else
expression. Putting it all together:
[67]: tape[current_position] = new_symbol
current_state = new_state
current_position = current_position + 1 if direction == 'R'\
else current_position - 1
current_symbol = tape[current_position]

tape
current_state
current_position
current_symbol

[67]: [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]

[67]: 'del2'

[67]: 4 只要有指令要执行,就应该一次又一次地执行,这取决于(current_state,current_symbol)
是否是TM_program的关键之一。为了更好地可视化计算,
[67]: 1 我们可以在计算的每个阶段输出磁带的图形,然后在下面输出current_state的值,
其最左边的字符正好位于头部所在单元格中的符号下方。让我们为此定义一个函数
This should be done again and again for as long as there is some instruction to execute, as deter-
mined by whether or not (current_state, current_symbol) is one of the keys of TM_program.
To better visualise computation, we can, at every stage of the computation, output a graphical
representation of tape and below, output the value of current_state with its leftmost character
right below the symbol in the cell that the head is positioned under. Let us define a function for
that purpose:

25
[68]: def display_current_configuration():
display_tape()
print(' ' * current_position, current_state)

Let us start from scratch and check that display_current_configuration() works as intended:

[69]: tape = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]


current_state = 'del1'
current_position = tape.index(1)
current_symbol = tape[current_position]
display_current_configuration()

---------------------------------
|0|0|0|1|1|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
del1
Let us now simulate the first 3 stages of the computation:
[70]: new_state, new_symbol, direction =\
TM_program[current_state, current_symbol]
tape[current_position] = new_symbol
current_state = new_state
current_position = current_position + 1 if direction == 'R'\
else current_position - 1
current_symbol = tape[current_position]
display_current_configuration()

---------------------------------
|0|0|0|0|1|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
del2

[71]: new_state, new_symbol, direction =\


TM_program[current_state, current_symbol]
tape[current_position] = new_symbol
current_state = new_state
current_position = current_position + 1 if direction == 'R'\
else current_position - 1
current_symbol = tape[current_position]
display_current_configuration()

---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov1R

26
[72]: new_state, new_symbol, direction =\
TM_program[current_state, current_symbol]
tape[current_position] = new_symbol
current_state = new_state
current_position = current_position + 1 if direction == 'R'\
else current_position - 1
current_symbol = tape[current_position]
display_current_configuration()

---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
while循环
mov1R
直到(current_state,current_symbol)还是TM_program的键
A while statement, another kind of loop, lets us execute those 6 statements again and again, for
as long as (current_state, current_symbol) is one of the keys of TM_program:

[73]: tape = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]


current_state = 'del1'
current_position = tape.index(1)
current_symbol = tape[current_position]
display_current_configuration()
while (current_state, current_symbol) in TM_program:
new_state, new_symbol, direction =\
TM_program[current_state, current_symbol]
tape[current_position] = new_symbol
current_state = new_state
current_position = current_position + 1 if direction == 'R'\
else current_position - 1
current_symbol = tape[current_position]
display_current_configuration()

---------------------------------
|0|0|0|1|1|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
del1
---------------------------------
|0|0|0|0|1|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
del2
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov1R

27
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|0|0|0|0|0|
---------------------------------
mov2R
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
mov1L
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2L

28
---------------------------------
|0|0|0|0|0|1|1|1|1|1|0|1|0|0|0|0|
---------------------------------
del1
---------------------------------
|0|0|0|0|0|0|1|1|1|1|0|1|0|0|0|0|
---------------------------------
del2
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2R
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0|0|
---------------------------------
mov2R
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|1|0|0|0|
---------------------------------
mov1L
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|1|0|0|0|
---------------------------------
mov1L
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|1|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|1|0|0|0|
---------------------------------
mov2L

29
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|1|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|1|0|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|0|0|1|1|1|0|1|1|0|0|0|
---------------------------------
del1
---------------------------------
|0|0|0|0|0|0|0|0|1|1|0|1|1|0|0|0|
---------------------------------
del2
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|0|0|0|
---------------------------------
mov1R
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|0|0|0|
---------------------------------
mov2R
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|0|0|0|
---------------------------------
mov2R
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|0|0|0|
---------------------------------
mov2R
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|1|0|0|
---------------------------------
mov1L
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|1|0|0|
---------------------------------
mov1L
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|1|0|0|
---------------------------------
mov1L

30
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|1|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|1|0|0|
---------------------------------
mov2L
---------------------------------
|0|0|0|0|0|0|0|0|0|1|0|1|1|1|0|0|
---------------------------------
del1
---------------------------------
|0|0|0|0|0|0|0|0|0|0|0|1|1|1|0|0|
---------------------------------
del2
---------------------------------
|0|0|0|0|0|0|0|0|0|0|0|1|1|1|0|0|
---------------------------------
end
Observe the following:
[74]: direction = 'R'
direction == 'R'
# direction == 'R' evaluates to True, which is then converted to 1
# for the arithmetic expression to make sense
2 * (direction == 'R') - 1
True的值是1
[74]: True False的值是0
[74]: 1

[75]: direction = 'L'


direction == 'R'
# direction == 'R' evaluates to False, which is then converted to 0
# for the arithmetic expression to make sense
2 * (direction == 'R') - 1

[75]: False

[75]: -1

This allows one to change the simulation loop as follows:


[76]: tape = [0, 0, 0, 1, 1, 0, 0, 0]
current_state = 'del1'
current_position = tape.index(1)

31
current_symbol = tape[current_position]
display_current_configuration()
while (current_state, current_symbol) in TM_program:
new_state, new_symbol, direction =\
TM_program[current_state, current_symbol]
tape[current_position] = new_symbol
current_state = new_state
# Alternative notation for
# current_position = current_position + 2 * (direction == 'R') - 1
current_position += 2 * (direction == 'R') - 1
current_symbol = tape[current_position]
display_current_configuration()

-----------------
|0|0|0|1|1|0|0|0|
-----------------
del1
-----------------
|0|0|0|0|1|0|0|0|
-----------------
del2
-----------------
|0|0|0|0|0|0|0|0|
-----------------
mov1R
-----------------
|0|0|0|0|0|0|0|0|
-----------------
mov2R
-----------------
|0|0|0|0|0|0|1|0|
-----------------
mov1L
-----------------
|0|0|0|0|0|0|1|0|
-----------------
mov2L
-----------------
|0|0|0|0|0|0|1|0|
-----------------
del1
-----------------
|0|0|0|0|0|0|1|0|
-----------------
end

32

You might also like