【问题标题】:Implementing a Turing machine in Erlang在 Erlang 中实现图灵机
【发布时间】:2013-12-08 14:00:02
【问题描述】:

我有一个与实现图灵机非常相似的小项目。我遇到的基本问题是保存当前配置,例如头部的位置和更多信息。对我来说重要的是特别节省头部位置以使他向前或向后移动。解决这个问题的 Erlang 方法是什么?

我是 Erlang 的新手,但就我探索 OTP 而言,gen_event 行为适合此。我的想法是越过初始头部位置,然后通过处理程序更改它。但我想还有更优雅的解决方案。

【问题讨论】:

    标签: configuration erlang state turing-machines


    【解决方案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 Behaviourgen_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 工作人员。

    【讨论】:

    • 感谢您的精彩回答,您成就了我的一天! ;)
    猜你喜欢
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2017-05-01
    • 2011-05-06
    • 2022-01-22
    • 2023-03-08
    • 1970-01-01
    • 2014-10-14
    相关资源
    最近更新 更多