patterncppMinor
Simulate Turing machine
Viewed 0 times
turingsimulatemachine
Problem
I have written this code to simulate a Turing machine and wonder what could be improved. In particular, the
```
#include
#include
#include
typedef enum {right, left} Direction;
template // n is the number of symbols
struct State {
std::array write; // Value to write, 0 move; // Direction to move tape
std::array next; // Next state, nullptr represents HALT
};
// Some test machines
std::array, 5> A = {{
{{1, 1}, {left, left}, {&A[1], &A[0]}},
{{1, 1}, {right, right}, {&A[2], &A[1]}},
{{1, 1}, {left, right}, {&A[0], &A[3]}},
{{1, 1}, {left, right}, {&A[0], &A[4]}},
{{1, 0}, {left, right}, {nullptr, &A[2]}},
}};
std::array, 6> B = {{
{{1, 1}, {left, left}, {&B[1], &B[0]}},
{{1, 1}, {right, right}, {&B[2], &B[1]}},
{{0, 1}, {right, right}, {&B[5], &B[3]}},
{{1, 0}, {left, right}, {&B[0], &B[4]}},
{{0, 1}, {left, right}, {&B[0], &B[2]}},
{{1, 1}, {left, left}, {&B[4], nullptr}},
}};
std::array, 3> C = {{
{{1, 1, 2}, {right, left, left}, {&C[1], &C[1], &C[0]}},
{{1, 1, 2}, {left, right, right}, {&C[0], &C[2], nullptr}},
{{0, 2, 1}, {left, right, left}, {&C[0], &C[2], &C[2]}},
}};
int main() {
std::list tape = {0};
std::list::iterator head = tape.begin();
State* current_state = &A[0];
int read;
unsigned long long int step_count = 0;
while (true) {
step_count += 1;
// Read tape
read = *head;
// Write to tape
*head = current_state->write[read];
// Move tape head
if (current_state->move[read] == right) {
if (head == --tape.end()) {
tape.push_back(0);
}
head++;
} else if (current_state->move[read] == left) {
if (head == tape.begin()) {
tape.push_front(0);
}
head--;
}
// Change state
if (current_state->next[read] == nul
--tape.end() seems a bit dodgy.```
#include
#include
#include
typedef enum {right, left} Direction;
template // n is the number of symbols
struct State {
std::array write; // Value to write, 0 move; // Direction to move tape
std::array next; // Next state, nullptr represents HALT
};
// Some test machines
std::array, 5> A = {{
{{1, 1}, {left, left}, {&A[1], &A[0]}},
{{1, 1}, {right, right}, {&A[2], &A[1]}},
{{1, 1}, {left, right}, {&A[0], &A[3]}},
{{1, 1}, {left, right}, {&A[0], &A[4]}},
{{1, 0}, {left, right}, {nullptr, &A[2]}},
}};
std::array, 6> B = {{
{{1, 1}, {left, left}, {&B[1], &B[0]}},
{{1, 1}, {right, right}, {&B[2], &B[1]}},
{{0, 1}, {right, right}, {&B[5], &B[3]}},
{{1, 0}, {left, right}, {&B[0], &B[4]}},
{{0, 1}, {left, right}, {&B[0], &B[2]}},
{{1, 1}, {left, left}, {&B[4], nullptr}},
}};
std::array, 3> C = {{
{{1, 1, 2}, {right, left, left}, {&C[1], &C[1], &C[0]}},
{{1, 1, 2}, {left, right, right}, {&C[0], &C[2], nullptr}},
{{0, 2, 1}, {left, right, left}, {&C[0], &C[2], &C[2]}},
}};
int main() {
std::list tape = {0};
std::list::iterator head = tape.begin();
State* current_state = &A[0];
int read;
unsigned long long int step_count = 0;
while (true) {
step_count += 1;
// Read tape
read = *head;
// Write to tape
*head = current_state->write[read];
// Move tape head
if (current_state->move[read] == right) {
if (head == --tape.end()) {
tape.push_back(0);
}
head++;
} else if (current_state->move[read] == left) {
if (head == tape.begin()) {
tape.push_front(0);
}
head--;
}
// Change state
if (current_state->next[read] == nul
Solution
I believe you're looking for
The linked page even states the exact comparison of the two:
Although the expression
I do have some additional points as well:
-
-
Since
In case you want to verify it right away, take a look at this test.
-
It may be more useful to use
This allows for less typing and for any iterator type (if it ever changes).
-
You could put these "test machines" in a separate file or somewhere separate from the main implementation. Proper unit testing may be preferred, but I don't have much to say about that. I'd also rename them as such as it's best not to use single-variable names outside of loop counters.
Another note: you can omit the
-
-
std::prev:std::prev(tape.end());The linked page even states the exact comparison of the two:
Although the expression
--c.end() often compiles, it is not guaranteed to do so: c.end() is an rvalue expression, and there is no iterator requirement that specifies that decrement of an rvalue is guaranteed to work. In particular, when iterators are implemented as pointers, --c.end() does not compile, while std::prev(c.end()) does.I do have some additional points as well:
-
typedef isn't needed for enums in C++.-
Since
tape is a storage container of a primitive type (ints), the default constructor will zero-initialize it automatically. As such, you don't need to assign to {0}.std::list tape;In case you want to verify it right away, take a look at this test.
-
It may be more useful to use
auto with head:auto head = tape.begin();This allows for less typing and for any iterator type (if it ever changes).
-
You could put these "test machines" in a separate file or somewhere separate from the main implementation. Proper unit testing may be preferred, but I don't have much to say about that. I'd also rename them as such as it's best not to use single-variable names outside of loop counters.
Another note: you can omit the
= for each std::array for C++11 list initialization.-
main() seems to be handling too much of the logic. Have other functions handle it and only have main() initialize variables, call these functions, then output the results.-
return 0 or similar isn't needed at the very end of main(); it'll always do the same return at this point.Code Snippets
std::prev(tape.end());std::list<int> tape;auto head = tape.begin();Context
StackExchange Code Review Q#131741, answer score: 5
Revisions (0)
No revisions yet.