在 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/3continue/3 和扩展版本go/5continue/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 中实现图灵机的主要内容,如果未能解决你的问题,请参考以下文章

《Erlang程序设计》点评赠书

BSV 上的图灵完备的“规则110”

BSV 上的图灵完备的“规则110”

如何在 Erlang 中实现以下循环?

图灵数学统计学丛书(1~53册)全集64本书

图灵机有“时间”的概念吗?