我认为实施取决于您的期望。您可以对机器状态进行纯功能实现。您还可以将转换表的纯功能实现作为函数或模块。最后,您可以使用或不使用 OTP 行为将其中的任何内容封装在进程中。
让我们从符号开始。它可以建模为原子,您可以选择空白的。它可以是原子'0'。它可以是一些花哨的名字blank。如你所愿。我们可以在turing.hrl中将其定义为常量。
-define(BLANK, '0').
让我们继续磁带。一种优雅的实现是使用众所周知的 zip 结构。它将是三元组{LEFT, HEAD, RIGHT}。
-module(tape).
-include("turing.hrl").
-export([new/0, read/1, write/2, left/1, right/1, tape2list/1]).
new() -> {[], ?BLANK, []}.
read({_, HEAD, _}) -> HEAD.
write({LEFT, _, RIGHT}, HEAD) -> {LEFT, HEAD, RIGHT}.
left({LEFT, HEAD, []}) -> {[HEAD|LEFT], ?BLANK, []};
left({LEFT, HEAD, [HR|RIGHT]}) -> {[HEAD|LEFT], HR, RIGHT}.
right({[], HEAD, RIGHT}) -> {[], ?BLANK, [HEAD|RIGHT]};
right({[HL|LEFT], HEAD, RIGHT}) -> {LEFT, HL, [HEAD|RIGHT]}.
tape2list({LEFT, HEAD, RIGHT}) -> lists:reverse(LEFT, [[HEAD]|RIGHT]).
现在我们可以进行机器实现了。让我们期望表实现为函数fun(STATE::any(), SYMBOL::any()) -> {NewSTATE::any(), NewSYMBOL::any(), 'left'|'right'} 和格式为 {STATE, TAPE} 的机器状态。所以转换可以建模为函数next/2。然后我们需要确定某个状态是否接受状态fun(STATE::any()) -> boolean() 的函数,然后我们可以提供模拟机器的函数,如go/3、continue/3 和扩展版本go/5 和continue/5,以及用于打印机器状态的附加参数.打印功能可以管理自己的状态。
-module(turing_machine).
-export([next/2, continue/5, continue/3, go/3, go/5, print_with_tape/2]).
next({STATE, TAPE}, F) when is_function(F, 2) ->
{NewSTATE, NewSYMBOL, Dir} = F(STATE, tape:read(TAPE)),
{NewSTATE, tape:Dir(tape:write(TAPE, NewSYMBOL))}.
continue({S, _} = St, Transition, IsAccepting, Print, PS) when
is_function(Transition, 2), is_function(IsAccepting, 1), is_function(Print, 2) ->
case IsAccepting(S) of
true -> St;
false ->
NSt = next(St, Transition),
continue(NSt, Transition, IsAccepting, Print, Print(NSt, PS))
end.
print({S, T}, _) ->
io:format("State: ~p, Head: ~p~n", [S, tape:read(T)]).
print_with_tape({S, T}, _) ->
io:format("State: ~p, Tape: ~p~n", [S, tape:tape2list(T)]).
continue(St, Transition, IsAccepting) ->
continue(St, Transition, IsAccepting, fun print/2, ok).
go(IS, Transition, IsAccepting) ->
go(IS, Transition, IsAccepting, fun print/2, ok).
go(IS, Transition, IsAccepting, Print, PS) ->
continue({IS, tape:new()}, Transition, IsAccepting, Print, PS).
那么我们就可以把忙碌的海狸机器当作函数
BB = fun
('A', '0') -> {'B', '1', right};
('A', '1') -> {'C', '1', left};
('B', '0') -> {'A', '1', left};
('B', '1') -> {'B', '1', right};
('C', '0') -> {'B', '1', left};
('C', '1') -> {'HALT', '1', right}
end.
BBA = fun(S) -> S =:= 'HALT' end.
然后运行:
> turing_machine:go('A', BB, BBA).
State: 'B', Head: '0'
State: 'A', Head: '1'
State: 'C', Head: '0'
State: 'B', Head: '0'
State: 'A', Head: '0'
State: 'B', Head: '1'
State: 'B', Head: '1'
State: 'B', Head: '1'
State: 'B', Head: '1'
State: 'B', Head: '0'
State: 'A', Head: '1'
State: 'C', Head: '1'
State: 'HALT', Head: '1'
{'HALT',{['1'],'1',['1','1','1','1']}}
或者更花哨的:
> turing_machine:go('A', BB, BBA, fun turing_machine:print_with_tape/2, ok).
State: 'B', Tape: [['0'],'1']
State: 'A', Tape: ['1',['1']]
State: 'C', Tape: ['1','1',['0']]
State: 'B', Tape: ['1','1','1',['0']]
State: 'A', Tape: ['1','1','1','1',['0']]
State: 'B', Tape: ['1','1','1',['1'],'1']
State: 'B', Tape: ['1','1',['1'],'1','1']
State: 'B', Tape: ['1',['1'],'1','1','1']
State: 'B', Tape: [['1'],'1','1','1','1']
State: 'B', Tape: [['0'],'1','1','1','1','1']
State: 'A', Tape: ['1',['1'],'1','1','1','1']
State: 'C', Tape: ['1','1',['1'],'1','1','1']
State: 'HALT', Tape: ['1',['1'],'1','1','1','1']
{'HALT',{['1'],'1',['1','1','1','1']}}
如果您希望将机器作为模块使用,您可以通过将回调添加到 turing_machine.erl 来定义行为 turing_machine
-callback init_st() -> St::any().
-callback transition(St::any(), Symb::any()) ->
{NewSt::any(), NewSymb::any(), left|right}.
-callback is_accepting(St::any()) -> boolean().
还有一些额外的导出函数
-export([go_mod/1, go_mod/3, continue_mod/2, continue_mod/4]).
以及它们的实现
go_mod(Mod) ->
go_mod(Mod, fun print/2, ok).
go_mod(Mod, Print, PS) ->
continue_mod(new_st(Mod:init_st()), Mod, Print, PS).
continue_mod(St, Mod) ->
continue_mod(St, Mod, fun print/2, ok).
continue_mod(St, Mod, Print, PS) ->
continue(St, fun Mod:transition/2, fun Mod:is_accepting/1, Print, PS).
比忙碌的海狸模块
-module(busy_beaver).
-behaviour(turing_machine).
-include("turing.hrl").
-define(B, ?BLANK).
-define(P, '1').
-export([init_st/0, transition/2, is_accepting/1]).
init_st() -> 'A'.
transition('A', ?B) -> {'B', ?P, right};
transition('A', ?P) -> {'C', ?P, left};
transition('B', ?B) -> {'A', ?P, left};
transition('B', ?P) -> {'B', ?P, right};
transition('C', ?B) -> {'B', ?P, left};
transition('C', ?P) -> {'HALT', ?P, right}.
is_accepting(St) -> St =:= 'HALT'.
然后就可以作为
> turing_machine:go_mod(busy_beaver).
State: 'B', Head: '0'
State: 'A', Head: '1'
State: 'C', Head: '0'
State: 'B', Head: '0'
State: 'A', Head: '0'
State: 'B', Head: '1'
State: 'B', Head: '1'
State: 'B', Head: '1'
State: 'B', Head: '1'
State: 'B', Head: '0'
State: 'A', Head: '1'
State: 'C', Head: '1'
State: 'HALT', Head: '1'
{'HALT',{['1'],'1',['1','1','1','1']}}
甚至
> turing_machine:go_mod(busy_beaver, fun turing_machine:print_with_tape/2, ok).
State: 'B', Tape: [['0'],'1']
State: 'A', Tape: ['1',['1']]
State: 'C', Tape: ['1','1',['0']]
State: 'B', Tape: ['1','1','1',['0']]
State: 'A', Tape: ['1','1','1','1',['0']]
State: 'B', Tape: ['1','1','1',['1'],'1']
State: 'B', Tape: ['1','1',['1'],'1','1']
State: 'B', Tape: ['1',['1'],'1','1','1']
State: 'B', Tape: [['1'],'1','1','1','1']
State: 'B', Tape: [['0'],'1','1','1','1','1']
State: 'A', Tape: ['1',['1'],'1','1','1','1']
State: 'C', Tape: ['1','1',['1'],'1','1','1']
State: 'HALT', Tape: ['1',['1'],'1','1','1','1']
{'HALT',{['1'],'1',['1','1','1','1']}}
然后您可以以一种或其他方式选择使其进程或 OTP 工作人员。