在 Erlang 中实现图灵机
Posted
技术标签:
【中文标题】在 Erlang 中实现图灵机【英文标题】:Implementing a Turing machine in Erlang 【发布时间】:2013-12-08 14:00:02 【问题描述】:我有一个与实现图灵机非常相似的小项目。我遇到的基本问题是保存当前配置,例如头部的位置和更多信息。对我来说重要的是特别节省头部位置以使他向前或向后移动。解决这个问题的 Erlang 方法是什么?
我是 Erlang 的新手,但就我探索 OTP 而言,gen_event 行为适合此。我的想法是越过初始头部位置,然后通过处理程序更改它。但我想还有更优雅的解决方案。
【问题讨论】:
【参考方案1】:在 Erlang 中,与在其他函数式语言中一样,您必须自己明确地管理您的状态。这意味着你必须随身携带它并通过你的代码穿线它。它IS比听起来容易得多,而且很快就会成为第二天性。
我个人会使用gen_server
行为而不是gen_event
。它更具体,将为您的机器提供更好的支持。 gen_event
比您需要的更通用。伊玛奥。
gen_server
行为,实际上所有的行为都为管理状态提供了支持。这些行为提供了顶层循环、接收和发送消息以及管理状态的基本功能。加上许多你想要的额外好东西,即使你还不知道。
您通过提供回调函数来接口gen_server
,所有行为,当事情发生时行为调用这些函数。您给出一个模块的名称,并且行为期望该模块包含回调。通常有固定数量的回调,例如gen_server
有 6 个,具有在特定时间调用的预定义名称。
例如有一个init/1
回调,它在服务器启动时被调用。它执行所有特定的初始化,然后返回ok,State
。这是您的服务器所需的状态。该行为对此进行管理并通过回调对其进行线程化,并期望一个新的作为回报。
例如,当您执行gen_server:call(Server, Message)
时,这将导致在服务器中调用handle_call/3
回调并使用以下参数和返回值:
handle_call(Message, From, State) --> reply,Reply,NewState
Reply
被发送回调用者,NewState
是更新后的状态,然后传递给下一个回调。
您可以在OTP Design Principles 中阅读更多相关信息,例如文档的Gen_Server Behaviour 和gen_server module 部分。
在你的情况下,你会让图灵机管理磁带、位置等,然后你会向它发送命令。 IMAO 再次。
【讨论】:
感谢您的精彩回答! gen_event 行为以哪种方式更普遍?或者最大的不同在哪里?仅通过查看文档,我认为 gen_server 非常适合双向通信,而 gen_event 更面向状态。【参考方案2】:我认为实施取决于您的期望。您可以对机器状态进行纯功能实现。您还可以将转换表的纯功能实现作为函数或模块。最后,您可以使用或不使用 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 工作人员。
【讨论】:
感谢您的精彩回答,您成就了我的一天! ;)以上是关于在 Erlang 中实现图灵机的主要内容,如果未能解决你的问题,请参考以下文章