HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Simulate Turing machine

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
turingsimulatemachine

Problem

I have written this code to simulate a Turing machine and wonder what could be improved. In particular, the --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 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.